Tutorial de Ruby on Rails
Ruby on Rails es la nueva generacioacuten en desarrollo de aplicaciones Web y junto con AJAXestaacute creando una revolucioacuten en la industria del desarrollo de software de web
Ruby y Rails
Ruby es un lenguaje de programacioacuten interpretado y orientado a objetos La filosofiacutea deRuby es dont repeat yourself (DRY) - no te repitas La idea de ruby es que usted nonecesita repetir lo que ya ha definido en otro lugar Esto hace a ruby muy compacto
Rails es un armazoacuten de libreriacuteas disentildeado especiacuteficamente para crear aplicaciones de webComo estaacute hecho en ruby es compatible con la filosofiacutea de DRY En vez de configuracioacutenRails prefiere convencioacuten y anotaciones Esto proviene principalmente de las frustracionescon plataformas que obligan a uno a repetir en archivos de configuracioacuten XML una historiaque ya se ha dicho en coacutedigo
Acerca del Curso
Este curso pretende proporcionar al usuario informacioacuten suficiente para hacer aplicacionesde web profesionales utilizando Ruby on Rails Estaacute enfocado a personas que ya sepan algode programacioacuten en otros lenguajes lo cual quiere decir que durante el curso hareacutereferencia a conceptos y libreriacuteas familiares a usuarios de otros lenguajes y productos
El curso se enfoca a crear una libreta de direcciones utilizando un servidor MySQL Duranteel curso tambieacuten trato de ensentildear conceptos relacionados como pruebas de unidadmetodologiacuteas aacutegiles y control de versiones
Optimizado para Firefox
Si usted utiliza un browser de la familia Mozilla con soporte paraXPath (como por ejemplo Firefox o Camino) usted podraacute ver unalista de enlaces de referencia en cada capiacutetulo que es generadaautomaacuteticamente Esta lista de enlaces no estacute disponible enInternet Explorer
Si no tiene Firefox puede instalar la version Firefox de Googlehaciendo click en el logo
Contenido
Introduccioacuten2 Instalando Rails en su plataforma3 Nuestra primera aplicacioacuten - creando un esqueleto 4 Teoriacutea El Paradigma MVC5 Teoriacutea Pruebas de Unidad (Unit Tests)6 Configurando la base de datos7 Creando un Moacutedulo con Modelo Controlador y Vista8 Control de versiones con subversion9 Pruebas de Unidad en Rails
10 Teoriacutea de Objetos11 Mapeo de Objetos a Relaciones12 Modificando el Moacutedulo generado - Vistas13 Buen disentildeo con CSS14 Utilizando Helpers
1
1 Introduccioacuten
El internet ha madurado bastante desde los diacuteas de los start-ups Ruby on Rails y Web 20son el resultado de dicho proceso de maduracioacuten
Condiciones Econoacutemicas
Los antildeos 1997 hasta el 2001 fueron lo que se llamoacute el dot com bubble Los internet start-ups- compantildeiacuteas pequentildeas con fondos iniciales enormes proporcionados por compantildeiacuteas decapital riesgoso muy entusiastas - crearon una revolucioacuten basada en las expectativas de lanueva tecnologiacutea y aunque muchas compantildeiacuteas fueron muy exitosas y lograron convertirestos fondos iniciales en una compantildeiacutea redituable a largo plazo muchas otras dejaron deexistir en la caiacuteda de la burbuja del NASDAQ Como toda burbuja tecnoloacutegica esto es partede un ciclo econoacutemico
ldquoYou never leave a recession on the same technology that youentered itrdquo ldquoNunca sales de una recesioacuten con la misma tecnologiacutea con laque entraste rdquoChairman Emeritus of the BoardIntel Corporation
Algo interesante que suele ocurrir en las partes bajas de los ciclos econoacutemicos es que hayuna reorganizacioacuten y re-evaluacioacuten de las tecnologiacuteas disponibles (lo que funcionoacute y lo queno valioacute la pena) y durante estas re-evaluaciones nuevas tecnologiacuteas y nuevas maneras desolucionar problemas aparecen lo cual transforma cualitativamente la tecnologiacutea yrecomienza el ciclo econoacutemico
Cambios en Metodologiacutea
Durante estos antildeos de la recesioacuten en tecnologiacutea los procesos tambieacuten fueron re-evaluadosy las metodologiacuteas aacutegiles de desarrollo aparecieron con personajes como Martin Fowler deThoughtWorks y Dave Thomas de Pragmatic Programmers
Eacutestas metodologiacuteas ponen eacutenfasis en producir software de alta calidad en corto tiempomediante comnunicacioacuten abierta Hablaremos mucho maacutes de metodologiacuteas aacutegiles maacutestarde
Web 20
Este cambio de las expectativas de calidad en la tecnologiacutea de internet estaacute siendo llamadobien o mal Web 20 Hay muchas opiniones al respecto pero lo que todo mundo parececomprender es que la maacutes reciente generacioacuten de aplicaciones en la red es muy diferente alas anteriores Tim OReily trata de explicar Web 20 como una serie de principios ypraacutecticas que incluyen ciertos patrones de disentildeo dinaacutemicos como RSS tagscolaboracioacuten AJAX etceacutetera
Todos estos principios y cambios en expectativas de calidad tanto en el software producidocomo en la metodologiacutea para producirlo requieren de un dinamismo mucho mayor a lo que
era necesario antes con lenguajes compilados como Java Asiacute que maacutes y maacutesdesarrolladores sufriendo los problemas de la tecnologiacutea existente y bajo presion por lanueva metodologiacutea y expectativas comenzaron a buscar alternativas
Durante esta buacutesqueda de alternativas un programador Daneacutes llamado David HeinemeierHansson utilizoacute el lenguaje Ruby para crear una aplicacioacuten llamada Basecamp Las libreriacuteasreutilizables que salieron de este proyecto es lo que hoy conocemos como Ruby on Rails
2 Instalando Rails
Este capiacutetulo explica coacutemo puede usted instalar Rails en su plataforma de desarrollo
Rails es un proyecto de fuente abierta y como tal hay varias maneras de instalarlo yobtenerlo dependiendo de su plataforma y sus necesidades
En este artiacuteculo cubriremos dos maneras de instalar rails
Para principiantesPara usuarios avanzados y ambientes de prueba pre-produccioacuten y produccioacuten
Para Desarrolladores
Si usted es un desarrollador de software la manera maacutes sencilla de utilizar rails es medianteuna solucioacuten todo-en-uno que no afecte su ambiente actual
Windows
Si usted utiliza Windows puede instalar la plataforma Rails completa (incluyendo unaversion de mysql) utilizando proyecto Instant Rails Eacuteste proyecto incluye apache ruby railsy el servidor de mysql todo listo para ejecutarse
Macintosh
Si utiliza Macintosh OS X puede utilizar el proyecto Locomotive Locomotive incluye rubyrails y sqllite (en vez de un servidor de mysql) Si usted tiene un servidor mysql en otramaacutequina locomotive incluye las libreriacuteas de mysql para conectarse
Estos ambientes de desarrollo son adecuados para comenzar a escribir aplicaciones enRails en su propia maacutequina pero no son adecuados para ambientes multiusuario Asiacute queuna vez que usted tenga su programa listo para que otras personas lo utilizen (ya sea enmodo de prueba pre-produccioacuten o produccioacuten) necesitaraacute comprender como instalar unambiente de rails desde cero
Componentes de Rails
Para efectos de instalacioacuten Rails se puede dividir en los siguientes componentes
Lenguaje Ruby El lenguaje en el cual fueacute escrito RailsLas libreriacuteas de Rails Rails se compone de varias libreriacuteas escritas en rubyLa base de datos Una base de datos relacional basada en SQL es indispensable para un sistema dedesarrollo de aplicaciones web moderno La mayor parte de los ejemplos utilizan MySQL
Ruby
Ruby es un lenguaje de programacioacuten interpretado Ruby funciona con cualquier sistemaoperativo moderno pero se siente maacutes en casa en sistemas tipo Unix
La manera en que usted instale ruby depende mucho de su tipo de maacutequina y sus
necesidades Es recomendable que lea todo lo relevante a la plataforma que desee instalarantes de que haga una decisioacuten acerca de coacutemo instalarlo
Ruby en Unix Linux y Mac OS X
Si usted tiene una maacutequina que proviene de la familia Unix (incluyendo Mac OS X) esprobable que ya tenga Ruby incluido Rails requiere de ruby 182 o 184 (pero 183 nofunciona)
Para verificar su versioacuten de ruby ejecute el comando ruby -v Si su versioacuten es adecuadausted puede proceder a la seccioacuten de Gems
Si su versioacuten de ruby no es compatible con rails usted tiene dos opciones
1 Checar si su sistema operativo necesita actualizacioacuten y la versioacuten actualizada incluye un nuevo Ruby En la actualidad los sistemas Linux tienden a tener los maacutes nuevas instalaciones de Ruby en binario En sistemas Solaris algunos usuarios utilizan los paquetes binarios de CSW blastwave para mantenersus sistemas solaris actualizados sin tener que instalar un compilador en la maacutequinaEn Mac OS X depende de su versioacuten del sistema operativo El wiki de RubyGarden tiene informacioacutenacerca de como instalar los binarios
2 Instalar ruby en usrlocal ya sea en binario o compilado
Nota Muchos administradores de sistema Unix (en especial deLinux) prefieren instalar los sistemas de web compilando losarchivos fuente Esto permite el control absoluto de su ambientepero requiere de mayor conocimiento de su sistema asiacute como uncompilador
Si usted decide instalar un sistema ruby desde coacutedigo fuente baje el coacutedigo fuente del sitiooficial de Ruby (en Mac OS X tambieacuten necesita instalar las herramientas XCode quenormalmente vienen incluidos en sus discos del sistema operativo) y utilice la maneraestaacutendar de compilar programas en Unix
$ cd ruby-184-src
$ configure --prefix=usrlocal
$ make$ make install
Despueacutes de instalarlo antildeada el directorio usrlocalrubybin a su variable PATH
Recuerde Si su maacutequina ya incluiacutea ruby el directorio de su nuevacopia de Ruby debe estar antes de los demaacutes directorios en suvariable PATH
Ahora verifique que su instalacioacuten funciona ejecutando ruby -v yverificando la nueva versioacuten
Ruby en Windows
Como mencioneacute anteriormente ruby se encuentra maacutes en casa en un sistema Unix Elproblema principal de Windows es que eacuteste sistema operativo no viene con un compilador -los usuarios de Windows se sienten maacutes comodos con paquetes en binarios y con instalador
Si usted no tiene deseo de aprender a manejar un sistema Unix usted puede utilizar el OneClick Ruby Installer for Windows Este es un instalador que incluye ruby muchas libreriacuteas enbinario y el libro Programming Ruby The Pragmatic Programmers Guide en un archivo de
ayuda Una vez instalado sencillamente ejecute una liacutenea de comando y escriba ruby -vpara verificar su versioacuten
Pero si usted quisiera instalar un subsistema tipo Unix bajo Windows (o si usted estaacuteobligado a usar Windows por su trabajo pero prefiere usar Linux en casa) usted puedeinstalar Cygwin que es una recompilacioacuten de las utileriacuteas GNU bajo windows Durante lainstalacioacuten seleccione Ruby para que se encuentre incluido
Ruby Gems
RubyGems es un manejador de libreriacuteas de Ruby que hace muy sencillo instalar libreriacuteasGems esta a punto de convertirse en parte de ruby Pero si gems no estaacute incluido en suversioacuten de ruby (lo cual puede verificar ejecutando gem -v para ver la versioacuten de gemsinstalada) lo necesita instalar Como usted ya tiene ruby el procedimiento para instalarlo esel mismo para todos los sistemas operativos
cd rubygems
ruby setuprb gem -v
Ahora que tiene ruby gems puede proceder a instalar rails
MySQL
Ahora necesita una base de datos Si usted utiliza Unix es importante instalarla antes deinstalar el paquete mysql de gems porque las libreriacuteas de cliente de mysql son necesarias
Para instalar mysql baje la versioacuten para su plataforma del sitio de internet de MySQLcomUna vez que la instale puede proceder a instalar rails
Rails
Ahora siacute podemos instalar rails Para instalar rails ejecute lo siguiente
$ sudo gem install rails --include-dependencies$ sudo gem install mysql --with-mysql-dir=usrlocalmysql
El paraacutemetro with-mysql-dir soacutelo es necesario si su mysql no instala correctamente (porqueel compilador no encontroacute las libreriacuteas de mysql) Aseguacuterese de que el directorio queutilizaen el paraacutemetro sea el directorio donde instaloacute mysql
Finalmente ejecute el comando rails --version para verificar que su versioacuten de rails fueacuteinstalada correctamente
3 Nuestra primera aplicacioacuten
Creando un Esqueleto
Uno comienza con su aplicacioacuten de Rails creando un esqueleto Crear un esqueleto es muyfacil
$ rails direcciones
create
create appapis
create appcontrollers
create apphelpers
create appmodels
create appviewslayouts
create configenvironments
create components
create db
create doc
create lib
create log
create publicimages
create publicjavascripts
create publicstylesheets
create script
create testfixtures
create testfunctional
create testmocksdevelopment
create testmockstest
create testunit
Facil no Ahora veamos lo que creamos Para esto necesitamos ejecutar un servidor Railspuede ser configurado con apache y FastCGI Pero nuestro esqueleto tambien cuenta conun pequentildeo servidor web Asiacute que lo ejecutamos
$ cd direcciones
$ ruby scriptserver
=gt Rails application started on http00003000
=gt Ctrl-C to shutdown server call with --help for options
[2005-12-06 232744] INFO WEBrick 131[2005-12-06 232744] INFO ruby 182 (2004-12-25) [i386-
mswin32][2005-12-06 232745] INFO WEBrickHTTPServerstart pid=3972
port=3000
Note el puerto que el servidor nos ha dedicado 3000 Este es el puerto que vamos aaccesar utilizando nuestro navegador favorito La direccion es localhost o 127001 Asiacute queen nuestro navegador iremos a http1270013000 Si todo salioacute bien veremos unapantalla de felicitaciones con instrucciones para configurar Apache y pasos siguientes
Ahora que tenemos nuestra aplicacioacuten de rails lista podemos comenzar a ver como estaacutecompuesta nuestra nueva aplicacioacuten
Tu ambiente
Rails 11 tiene un enlace llamado About yourApplications environment que le permite verificarinformacioacuten acerca del ambiente Esta informacioacuten seencuentra en el URL rails_infoproperties
Anatomiacutea de una Aplicacioacuten Rails
Una aplicacioacuten rails se encuentra en el subdirectorio app y se compone de los siguientesdirectorios
apis - las libreriacuteas que su programa requiere fuera de Rails mismocontrollers - Los controladoreshelpers - Los helpersmodels - Los modelos Basicamente las clases que representan los datos que nuestra aplicacioacutenmanipularaacuteviews - Las vistas que son archivos rhtml (como JSP o ASP)
viewslayouts - Los disentildeos Cada controlador tiene su propio disentildeo donde se pondran las cabeceras ypies de paacuteginaconfig - Archivos de configuracioacuten La configuracioacuten de la base de datos se encuentra aquiacutescript - Utileriacuteas para generar coacutedigo y ejecutar el servidorpublic - La raiacutez del directorio virtual Cualquier contenido que usted encuentre aquiacute seraacute publicado en suaplicacioacuten directamente En una nueva aplicacioacuten usted puede encontrar las paacuteginas web para loserrores 404 y 500 las imaacutegenes javascripts estilos etceacuteteratest - Los archivos para las pruebas funcionales y de unidad
Llamando coacutedigo
Seguramente querremos que nuestro directorio tenga una pantalla principalConceptualmente llamaremos a esto el moacutedulo principal Asiacute que lo generamos
$ ruby scriptgenerate controller principal
exists appcontrollers
exists apphelpers
create appviewsprincipal
exists testfunctional
create appcontrollersprincipal_controllerrb
create testfunctionalprincipal_controller_testrb
create apphelpersprincipal_helperrb
Ahora puede usted visitar http1270013000principal para ver
Unknown actionNo action responded to index
El archivo que fue creado se llama controllersprincipal_controllerrb Vamos aponer nuestro coacutedigo ahiacute
class PrincipalController lt ApplicationController
def index
mensaje = Hola a todosltbrgt
[Pedro Pablo Juan]each do |nombre|
mensaje += y a +nombre+ tambienltbrgt
end
render text =gt mensaje
end
end
Cuando visitamos de nuevo vemos nuestro resultado
Hola a todosy a Pedro tambieny a Pablo tambieny a Juan tambien
Finalmente queremos que la raiacutez de esta aplicacioacuten sea tambieacuten nuestro controlador Asiacuteque eliminamos el indexhtml de bienvenida
$ rm publicindexhtml
Si vemos la paacutegina de nuevo ahora diraacute
Routing ErrorRecognition failed for
Esto es porque todaviacutea no le hemos dicho a la aplicacioacuten que nuestro controlador pordefecto es el principal Podemos hacer esto definiendo una ruta Asiacute que modificamos laconfiguracioacuten de rutas en el archivo configroutesrb
mapconnect controller =gt principal
Ahora cuando visitamos la paacutegina vemos el resultado de nuestro controlador principal
Si tiene dudas puede ver la peliacutecula
4 Teoriacutea El Paradigma MVC
MVC son las siglas de Modelo Vista y Controlador Es el patroacuten de disentildeo de software muacuteycomuacuten en programas interactivos orientados a objetos
Bajo el patroacuten de disentildeo de MVC dentro de un programa cada dominio loacutegico de edicioacuten(por ejemplo datos personales cuentas bancarias artiacuteculos noticiosos etceacutetera) necesitatres partes
1 El modelo que es una representacioacuten de objetos del dominio loacutegico En el ejemplo de datos personalesobjetos como Persona Direccioacuten y Teleacutefono
2 La vista que es una o varias piezas de coacutedigo que formatean y muestran todo o parte del modelo en eldispositivo interactivo (tiacutepicamente la pantalla) La vista tiacutepicamente es notificada de cambios en elmodelo
3 El controlador que interpreta la informacioacuten que el usuario provee para hacer cambios en el modelo
La idea es que al aplicar esta separacioacuten se hace posible crear maacutes de una vista para elmismo modelo (digamos una vista abreviada y una vista detallada) y reutilizar el modelo (yel coacutedigo que guarda el modelo de manera permanente) para escribir utileriacuteas relacionadaso incorporar datos del dominio original en programas maacutes grandes
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Contenido
Introduccioacuten2 Instalando Rails en su plataforma3 Nuestra primera aplicacioacuten - creando un esqueleto 4 Teoriacutea El Paradigma MVC5 Teoriacutea Pruebas de Unidad (Unit Tests)6 Configurando la base de datos7 Creando un Moacutedulo con Modelo Controlador y Vista8 Control de versiones con subversion9 Pruebas de Unidad en Rails
10 Teoriacutea de Objetos11 Mapeo de Objetos a Relaciones12 Modificando el Moacutedulo generado - Vistas13 Buen disentildeo con CSS14 Utilizando Helpers
1
1 Introduccioacuten
El internet ha madurado bastante desde los diacuteas de los start-ups Ruby on Rails y Web 20son el resultado de dicho proceso de maduracioacuten
Condiciones Econoacutemicas
Los antildeos 1997 hasta el 2001 fueron lo que se llamoacute el dot com bubble Los internet start-ups- compantildeiacuteas pequentildeas con fondos iniciales enormes proporcionados por compantildeiacuteas decapital riesgoso muy entusiastas - crearon una revolucioacuten basada en las expectativas de lanueva tecnologiacutea y aunque muchas compantildeiacuteas fueron muy exitosas y lograron convertirestos fondos iniciales en una compantildeiacutea redituable a largo plazo muchas otras dejaron deexistir en la caiacuteda de la burbuja del NASDAQ Como toda burbuja tecnoloacutegica esto es partede un ciclo econoacutemico
ldquoYou never leave a recession on the same technology that youentered itrdquo ldquoNunca sales de una recesioacuten con la misma tecnologiacutea con laque entraste rdquoChairman Emeritus of the BoardIntel Corporation
Algo interesante que suele ocurrir en las partes bajas de los ciclos econoacutemicos es que hayuna reorganizacioacuten y re-evaluacioacuten de las tecnologiacuteas disponibles (lo que funcionoacute y lo queno valioacute la pena) y durante estas re-evaluaciones nuevas tecnologiacuteas y nuevas maneras desolucionar problemas aparecen lo cual transforma cualitativamente la tecnologiacutea yrecomienza el ciclo econoacutemico
Cambios en Metodologiacutea
Durante estos antildeos de la recesioacuten en tecnologiacutea los procesos tambieacuten fueron re-evaluadosy las metodologiacuteas aacutegiles de desarrollo aparecieron con personajes como Martin Fowler deThoughtWorks y Dave Thomas de Pragmatic Programmers
Eacutestas metodologiacuteas ponen eacutenfasis en producir software de alta calidad en corto tiempomediante comnunicacioacuten abierta Hablaremos mucho maacutes de metodologiacuteas aacutegiles maacutestarde
Web 20
Este cambio de las expectativas de calidad en la tecnologiacutea de internet estaacute siendo llamadobien o mal Web 20 Hay muchas opiniones al respecto pero lo que todo mundo parececomprender es que la maacutes reciente generacioacuten de aplicaciones en la red es muy diferente alas anteriores Tim OReily trata de explicar Web 20 como una serie de principios ypraacutecticas que incluyen ciertos patrones de disentildeo dinaacutemicos como RSS tagscolaboracioacuten AJAX etceacutetera
Todos estos principios y cambios en expectativas de calidad tanto en el software producidocomo en la metodologiacutea para producirlo requieren de un dinamismo mucho mayor a lo que
era necesario antes con lenguajes compilados como Java Asiacute que maacutes y maacutesdesarrolladores sufriendo los problemas de la tecnologiacutea existente y bajo presion por lanueva metodologiacutea y expectativas comenzaron a buscar alternativas
Durante esta buacutesqueda de alternativas un programador Daneacutes llamado David HeinemeierHansson utilizoacute el lenguaje Ruby para crear una aplicacioacuten llamada Basecamp Las libreriacuteasreutilizables que salieron de este proyecto es lo que hoy conocemos como Ruby on Rails
2 Instalando Rails
Este capiacutetulo explica coacutemo puede usted instalar Rails en su plataforma de desarrollo
Rails es un proyecto de fuente abierta y como tal hay varias maneras de instalarlo yobtenerlo dependiendo de su plataforma y sus necesidades
En este artiacuteculo cubriremos dos maneras de instalar rails
Para principiantesPara usuarios avanzados y ambientes de prueba pre-produccioacuten y produccioacuten
Para Desarrolladores
Si usted es un desarrollador de software la manera maacutes sencilla de utilizar rails es medianteuna solucioacuten todo-en-uno que no afecte su ambiente actual
Windows
Si usted utiliza Windows puede instalar la plataforma Rails completa (incluyendo unaversion de mysql) utilizando proyecto Instant Rails Eacuteste proyecto incluye apache ruby railsy el servidor de mysql todo listo para ejecutarse
Macintosh
Si utiliza Macintosh OS X puede utilizar el proyecto Locomotive Locomotive incluye rubyrails y sqllite (en vez de un servidor de mysql) Si usted tiene un servidor mysql en otramaacutequina locomotive incluye las libreriacuteas de mysql para conectarse
Estos ambientes de desarrollo son adecuados para comenzar a escribir aplicaciones enRails en su propia maacutequina pero no son adecuados para ambientes multiusuario Asiacute queuna vez que usted tenga su programa listo para que otras personas lo utilizen (ya sea enmodo de prueba pre-produccioacuten o produccioacuten) necesitaraacute comprender como instalar unambiente de rails desde cero
Componentes de Rails
Para efectos de instalacioacuten Rails se puede dividir en los siguientes componentes
Lenguaje Ruby El lenguaje en el cual fueacute escrito RailsLas libreriacuteas de Rails Rails se compone de varias libreriacuteas escritas en rubyLa base de datos Una base de datos relacional basada en SQL es indispensable para un sistema dedesarrollo de aplicaciones web moderno La mayor parte de los ejemplos utilizan MySQL
Ruby
Ruby es un lenguaje de programacioacuten interpretado Ruby funciona con cualquier sistemaoperativo moderno pero se siente maacutes en casa en sistemas tipo Unix
La manera en que usted instale ruby depende mucho de su tipo de maacutequina y sus
necesidades Es recomendable que lea todo lo relevante a la plataforma que desee instalarantes de que haga una decisioacuten acerca de coacutemo instalarlo
Ruby en Unix Linux y Mac OS X
Si usted tiene una maacutequina que proviene de la familia Unix (incluyendo Mac OS X) esprobable que ya tenga Ruby incluido Rails requiere de ruby 182 o 184 (pero 183 nofunciona)
Para verificar su versioacuten de ruby ejecute el comando ruby -v Si su versioacuten es adecuadausted puede proceder a la seccioacuten de Gems
Si su versioacuten de ruby no es compatible con rails usted tiene dos opciones
1 Checar si su sistema operativo necesita actualizacioacuten y la versioacuten actualizada incluye un nuevo Ruby En la actualidad los sistemas Linux tienden a tener los maacutes nuevas instalaciones de Ruby en binario En sistemas Solaris algunos usuarios utilizan los paquetes binarios de CSW blastwave para mantenersus sistemas solaris actualizados sin tener que instalar un compilador en la maacutequinaEn Mac OS X depende de su versioacuten del sistema operativo El wiki de RubyGarden tiene informacioacutenacerca de como instalar los binarios
2 Instalar ruby en usrlocal ya sea en binario o compilado
Nota Muchos administradores de sistema Unix (en especial deLinux) prefieren instalar los sistemas de web compilando losarchivos fuente Esto permite el control absoluto de su ambientepero requiere de mayor conocimiento de su sistema asiacute como uncompilador
Si usted decide instalar un sistema ruby desde coacutedigo fuente baje el coacutedigo fuente del sitiooficial de Ruby (en Mac OS X tambieacuten necesita instalar las herramientas XCode quenormalmente vienen incluidos en sus discos del sistema operativo) y utilice la maneraestaacutendar de compilar programas en Unix
$ cd ruby-184-src
$ configure --prefix=usrlocal
$ make$ make install
Despueacutes de instalarlo antildeada el directorio usrlocalrubybin a su variable PATH
Recuerde Si su maacutequina ya incluiacutea ruby el directorio de su nuevacopia de Ruby debe estar antes de los demaacutes directorios en suvariable PATH
Ahora verifique que su instalacioacuten funciona ejecutando ruby -v yverificando la nueva versioacuten
Ruby en Windows
Como mencioneacute anteriormente ruby se encuentra maacutes en casa en un sistema Unix Elproblema principal de Windows es que eacuteste sistema operativo no viene con un compilador -los usuarios de Windows se sienten maacutes comodos con paquetes en binarios y con instalador
Si usted no tiene deseo de aprender a manejar un sistema Unix usted puede utilizar el OneClick Ruby Installer for Windows Este es un instalador que incluye ruby muchas libreriacuteas enbinario y el libro Programming Ruby The Pragmatic Programmers Guide en un archivo de
ayuda Una vez instalado sencillamente ejecute una liacutenea de comando y escriba ruby -vpara verificar su versioacuten
Pero si usted quisiera instalar un subsistema tipo Unix bajo Windows (o si usted estaacuteobligado a usar Windows por su trabajo pero prefiere usar Linux en casa) usted puedeinstalar Cygwin que es una recompilacioacuten de las utileriacuteas GNU bajo windows Durante lainstalacioacuten seleccione Ruby para que se encuentre incluido
Ruby Gems
RubyGems es un manejador de libreriacuteas de Ruby que hace muy sencillo instalar libreriacuteasGems esta a punto de convertirse en parte de ruby Pero si gems no estaacute incluido en suversioacuten de ruby (lo cual puede verificar ejecutando gem -v para ver la versioacuten de gemsinstalada) lo necesita instalar Como usted ya tiene ruby el procedimiento para instalarlo esel mismo para todos los sistemas operativos
cd rubygems
ruby setuprb gem -v
Ahora que tiene ruby gems puede proceder a instalar rails
MySQL
Ahora necesita una base de datos Si usted utiliza Unix es importante instalarla antes deinstalar el paquete mysql de gems porque las libreriacuteas de cliente de mysql son necesarias
Para instalar mysql baje la versioacuten para su plataforma del sitio de internet de MySQLcomUna vez que la instale puede proceder a instalar rails
Rails
Ahora siacute podemos instalar rails Para instalar rails ejecute lo siguiente
$ sudo gem install rails --include-dependencies$ sudo gem install mysql --with-mysql-dir=usrlocalmysql
El paraacutemetro with-mysql-dir soacutelo es necesario si su mysql no instala correctamente (porqueel compilador no encontroacute las libreriacuteas de mysql) Aseguacuterese de que el directorio queutilizaen el paraacutemetro sea el directorio donde instaloacute mysql
Finalmente ejecute el comando rails --version para verificar que su versioacuten de rails fueacuteinstalada correctamente
3 Nuestra primera aplicacioacuten
Creando un Esqueleto
Uno comienza con su aplicacioacuten de Rails creando un esqueleto Crear un esqueleto es muyfacil
$ rails direcciones
create
create appapis
create appcontrollers
create apphelpers
create appmodels
create appviewslayouts
create configenvironments
create components
create db
create doc
create lib
create log
create publicimages
create publicjavascripts
create publicstylesheets
create script
create testfixtures
create testfunctional
create testmocksdevelopment
create testmockstest
create testunit
Facil no Ahora veamos lo que creamos Para esto necesitamos ejecutar un servidor Railspuede ser configurado con apache y FastCGI Pero nuestro esqueleto tambien cuenta conun pequentildeo servidor web Asiacute que lo ejecutamos
$ cd direcciones
$ ruby scriptserver
=gt Rails application started on http00003000
=gt Ctrl-C to shutdown server call with --help for options
[2005-12-06 232744] INFO WEBrick 131[2005-12-06 232744] INFO ruby 182 (2004-12-25) [i386-
mswin32][2005-12-06 232745] INFO WEBrickHTTPServerstart pid=3972
port=3000
Note el puerto que el servidor nos ha dedicado 3000 Este es el puerto que vamos aaccesar utilizando nuestro navegador favorito La direccion es localhost o 127001 Asiacute queen nuestro navegador iremos a http1270013000 Si todo salioacute bien veremos unapantalla de felicitaciones con instrucciones para configurar Apache y pasos siguientes
Ahora que tenemos nuestra aplicacioacuten de rails lista podemos comenzar a ver como estaacutecompuesta nuestra nueva aplicacioacuten
Tu ambiente
Rails 11 tiene un enlace llamado About yourApplications environment que le permite verificarinformacioacuten acerca del ambiente Esta informacioacuten seencuentra en el URL rails_infoproperties
Anatomiacutea de una Aplicacioacuten Rails
Una aplicacioacuten rails se encuentra en el subdirectorio app y se compone de los siguientesdirectorios
apis - las libreriacuteas que su programa requiere fuera de Rails mismocontrollers - Los controladoreshelpers - Los helpersmodels - Los modelos Basicamente las clases que representan los datos que nuestra aplicacioacutenmanipularaacuteviews - Las vistas que son archivos rhtml (como JSP o ASP)
viewslayouts - Los disentildeos Cada controlador tiene su propio disentildeo donde se pondran las cabeceras ypies de paacuteginaconfig - Archivos de configuracioacuten La configuracioacuten de la base de datos se encuentra aquiacutescript - Utileriacuteas para generar coacutedigo y ejecutar el servidorpublic - La raiacutez del directorio virtual Cualquier contenido que usted encuentre aquiacute seraacute publicado en suaplicacioacuten directamente En una nueva aplicacioacuten usted puede encontrar las paacuteginas web para loserrores 404 y 500 las imaacutegenes javascripts estilos etceacuteteratest - Los archivos para las pruebas funcionales y de unidad
Llamando coacutedigo
Seguramente querremos que nuestro directorio tenga una pantalla principalConceptualmente llamaremos a esto el moacutedulo principal Asiacute que lo generamos
$ ruby scriptgenerate controller principal
exists appcontrollers
exists apphelpers
create appviewsprincipal
exists testfunctional
create appcontrollersprincipal_controllerrb
create testfunctionalprincipal_controller_testrb
create apphelpersprincipal_helperrb
Ahora puede usted visitar http1270013000principal para ver
Unknown actionNo action responded to index
El archivo que fue creado se llama controllersprincipal_controllerrb Vamos aponer nuestro coacutedigo ahiacute
class PrincipalController lt ApplicationController
def index
mensaje = Hola a todosltbrgt
[Pedro Pablo Juan]each do |nombre|
mensaje += y a +nombre+ tambienltbrgt
end
render text =gt mensaje
end
end
Cuando visitamos de nuevo vemos nuestro resultado
Hola a todosy a Pedro tambieny a Pablo tambieny a Juan tambien
Finalmente queremos que la raiacutez de esta aplicacioacuten sea tambieacuten nuestro controlador Asiacuteque eliminamos el indexhtml de bienvenida
$ rm publicindexhtml
Si vemos la paacutegina de nuevo ahora diraacute
Routing ErrorRecognition failed for
Esto es porque todaviacutea no le hemos dicho a la aplicacioacuten que nuestro controlador pordefecto es el principal Podemos hacer esto definiendo una ruta Asiacute que modificamos laconfiguracioacuten de rutas en el archivo configroutesrb
mapconnect controller =gt principal
Ahora cuando visitamos la paacutegina vemos el resultado de nuestro controlador principal
Si tiene dudas puede ver la peliacutecula
4 Teoriacutea El Paradigma MVC
MVC son las siglas de Modelo Vista y Controlador Es el patroacuten de disentildeo de software muacuteycomuacuten en programas interactivos orientados a objetos
Bajo el patroacuten de disentildeo de MVC dentro de un programa cada dominio loacutegico de edicioacuten(por ejemplo datos personales cuentas bancarias artiacuteculos noticiosos etceacutetera) necesitatres partes
1 El modelo que es una representacioacuten de objetos del dominio loacutegico En el ejemplo de datos personalesobjetos como Persona Direccioacuten y Teleacutefono
2 La vista que es una o varias piezas de coacutedigo que formatean y muestran todo o parte del modelo en eldispositivo interactivo (tiacutepicamente la pantalla) La vista tiacutepicamente es notificada de cambios en elmodelo
3 El controlador que interpreta la informacioacuten que el usuario provee para hacer cambios en el modelo
La idea es que al aplicar esta separacioacuten se hace posible crear maacutes de una vista para elmismo modelo (digamos una vista abreviada y una vista detallada) y reutilizar el modelo (yel coacutedigo que guarda el modelo de manera permanente) para escribir utileriacuteas relacionadaso incorporar datos del dominio original en programas maacutes grandes
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
1 Introduccioacuten
El internet ha madurado bastante desde los diacuteas de los start-ups Ruby on Rails y Web 20son el resultado de dicho proceso de maduracioacuten
Condiciones Econoacutemicas
Los antildeos 1997 hasta el 2001 fueron lo que se llamoacute el dot com bubble Los internet start-ups- compantildeiacuteas pequentildeas con fondos iniciales enormes proporcionados por compantildeiacuteas decapital riesgoso muy entusiastas - crearon una revolucioacuten basada en las expectativas de lanueva tecnologiacutea y aunque muchas compantildeiacuteas fueron muy exitosas y lograron convertirestos fondos iniciales en una compantildeiacutea redituable a largo plazo muchas otras dejaron deexistir en la caiacuteda de la burbuja del NASDAQ Como toda burbuja tecnoloacutegica esto es partede un ciclo econoacutemico
ldquoYou never leave a recession on the same technology that youentered itrdquo ldquoNunca sales de una recesioacuten con la misma tecnologiacutea con laque entraste rdquoChairman Emeritus of the BoardIntel Corporation
Algo interesante que suele ocurrir en las partes bajas de los ciclos econoacutemicos es que hayuna reorganizacioacuten y re-evaluacioacuten de las tecnologiacuteas disponibles (lo que funcionoacute y lo queno valioacute la pena) y durante estas re-evaluaciones nuevas tecnologiacuteas y nuevas maneras desolucionar problemas aparecen lo cual transforma cualitativamente la tecnologiacutea yrecomienza el ciclo econoacutemico
Cambios en Metodologiacutea
Durante estos antildeos de la recesioacuten en tecnologiacutea los procesos tambieacuten fueron re-evaluadosy las metodologiacuteas aacutegiles de desarrollo aparecieron con personajes como Martin Fowler deThoughtWorks y Dave Thomas de Pragmatic Programmers
Eacutestas metodologiacuteas ponen eacutenfasis en producir software de alta calidad en corto tiempomediante comnunicacioacuten abierta Hablaremos mucho maacutes de metodologiacuteas aacutegiles maacutestarde
Web 20
Este cambio de las expectativas de calidad en la tecnologiacutea de internet estaacute siendo llamadobien o mal Web 20 Hay muchas opiniones al respecto pero lo que todo mundo parececomprender es que la maacutes reciente generacioacuten de aplicaciones en la red es muy diferente alas anteriores Tim OReily trata de explicar Web 20 como una serie de principios ypraacutecticas que incluyen ciertos patrones de disentildeo dinaacutemicos como RSS tagscolaboracioacuten AJAX etceacutetera
Todos estos principios y cambios en expectativas de calidad tanto en el software producidocomo en la metodologiacutea para producirlo requieren de un dinamismo mucho mayor a lo que
era necesario antes con lenguajes compilados como Java Asiacute que maacutes y maacutesdesarrolladores sufriendo los problemas de la tecnologiacutea existente y bajo presion por lanueva metodologiacutea y expectativas comenzaron a buscar alternativas
Durante esta buacutesqueda de alternativas un programador Daneacutes llamado David HeinemeierHansson utilizoacute el lenguaje Ruby para crear una aplicacioacuten llamada Basecamp Las libreriacuteasreutilizables que salieron de este proyecto es lo que hoy conocemos como Ruby on Rails
2 Instalando Rails
Este capiacutetulo explica coacutemo puede usted instalar Rails en su plataforma de desarrollo
Rails es un proyecto de fuente abierta y como tal hay varias maneras de instalarlo yobtenerlo dependiendo de su plataforma y sus necesidades
En este artiacuteculo cubriremos dos maneras de instalar rails
Para principiantesPara usuarios avanzados y ambientes de prueba pre-produccioacuten y produccioacuten
Para Desarrolladores
Si usted es un desarrollador de software la manera maacutes sencilla de utilizar rails es medianteuna solucioacuten todo-en-uno que no afecte su ambiente actual
Windows
Si usted utiliza Windows puede instalar la plataforma Rails completa (incluyendo unaversion de mysql) utilizando proyecto Instant Rails Eacuteste proyecto incluye apache ruby railsy el servidor de mysql todo listo para ejecutarse
Macintosh
Si utiliza Macintosh OS X puede utilizar el proyecto Locomotive Locomotive incluye rubyrails y sqllite (en vez de un servidor de mysql) Si usted tiene un servidor mysql en otramaacutequina locomotive incluye las libreriacuteas de mysql para conectarse
Estos ambientes de desarrollo son adecuados para comenzar a escribir aplicaciones enRails en su propia maacutequina pero no son adecuados para ambientes multiusuario Asiacute queuna vez que usted tenga su programa listo para que otras personas lo utilizen (ya sea enmodo de prueba pre-produccioacuten o produccioacuten) necesitaraacute comprender como instalar unambiente de rails desde cero
Componentes de Rails
Para efectos de instalacioacuten Rails se puede dividir en los siguientes componentes
Lenguaje Ruby El lenguaje en el cual fueacute escrito RailsLas libreriacuteas de Rails Rails se compone de varias libreriacuteas escritas en rubyLa base de datos Una base de datos relacional basada en SQL es indispensable para un sistema dedesarrollo de aplicaciones web moderno La mayor parte de los ejemplos utilizan MySQL
Ruby
Ruby es un lenguaje de programacioacuten interpretado Ruby funciona con cualquier sistemaoperativo moderno pero se siente maacutes en casa en sistemas tipo Unix
La manera en que usted instale ruby depende mucho de su tipo de maacutequina y sus
necesidades Es recomendable que lea todo lo relevante a la plataforma que desee instalarantes de que haga una decisioacuten acerca de coacutemo instalarlo
Ruby en Unix Linux y Mac OS X
Si usted tiene una maacutequina que proviene de la familia Unix (incluyendo Mac OS X) esprobable que ya tenga Ruby incluido Rails requiere de ruby 182 o 184 (pero 183 nofunciona)
Para verificar su versioacuten de ruby ejecute el comando ruby -v Si su versioacuten es adecuadausted puede proceder a la seccioacuten de Gems
Si su versioacuten de ruby no es compatible con rails usted tiene dos opciones
1 Checar si su sistema operativo necesita actualizacioacuten y la versioacuten actualizada incluye un nuevo Ruby En la actualidad los sistemas Linux tienden a tener los maacutes nuevas instalaciones de Ruby en binario En sistemas Solaris algunos usuarios utilizan los paquetes binarios de CSW blastwave para mantenersus sistemas solaris actualizados sin tener que instalar un compilador en la maacutequinaEn Mac OS X depende de su versioacuten del sistema operativo El wiki de RubyGarden tiene informacioacutenacerca de como instalar los binarios
2 Instalar ruby en usrlocal ya sea en binario o compilado
Nota Muchos administradores de sistema Unix (en especial deLinux) prefieren instalar los sistemas de web compilando losarchivos fuente Esto permite el control absoluto de su ambientepero requiere de mayor conocimiento de su sistema asiacute como uncompilador
Si usted decide instalar un sistema ruby desde coacutedigo fuente baje el coacutedigo fuente del sitiooficial de Ruby (en Mac OS X tambieacuten necesita instalar las herramientas XCode quenormalmente vienen incluidos en sus discos del sistema operativo) y utilice la maneraestaacutendar de compilar programas en Unix
$ cd ruby-184-src
$ configure --prefix=usrlocal
$ make$ make install
Despueacutes de instalarlo antildeada el directorio usrlocalrubybin a su variable PATH
Recuerde Si su maacutequina ya incluiacutea ruby el directorio de su nuevacopia de Ruby debe estar antes de los demaacutes directorios en suvariable PATH
Ahora verifique que su instalacioacuten funciona ejecutando ruby -v yverificando la nueva versioacuten
Ruby en Windows
Como mencioneacute anteriormente ruby se encuentra maacutes en casa en un sistema Unix Elproblema principal de Windows es que eacuteste sistema operativo no viene con un compilador -los usuarios de Windows se sienten maacutes comodos con paquetes en binarios y con instalador
Si usted no tiene deseo de aprender a manejar un sistema Unix usted puede utilizar el OneClick Ruby Installer for Windows Este es un instalador que incluye ruby muchas libreriacuteas enbinario y el libro Programming Ruby The Pragmatic Programmers Guide en un archivo de
ayuda Una vez instalado sencillamente ejecute una liacutenea de comando y escriba ruby -vpara verificar su versioacuten
Pero si usted quisiera instalar un subsistema tipo Unix bajo Windows (o si usted estaacuteobligado a usar Windows por su trabajo pero prefiere usar Linux en casa) usted puedeinstalar Cygwin que es una recompilacioacuten de las utileriacuteas GNU bajo windows Durante lainstalacioacuten seleccione Ruby para que se encuentre incluido
Ruby Gems
RubyGems es un manejador de libreriacuteas de Ruby que hace muy sencillo instalar libreriacuteasGems esta a punto de convertirse en parte de ruby Pero si gems no estaacute incluido en suversioacuten de ruby (lo cual puede verificar ejecutando gem -v para ver la versioacuten de gemsinstalada) lo necesita instalar Como usted ya tiene ruby el procedimiento para instalarlo esel mismo para todos los sistemas operativos
cd rubygems
ruby setuprb gem -v
Ahora que tiene ruby gems puede proceder a instalar rails
MySQL
Ahora necesita una base de datos Si usted utiliza Unix es importante instalarla antes deinstalar el paquete mysql de gems porque las libreriacuteas de cliente de mysql son necesarias
Para instalar mysql baje la versioacuten para su plataforma del sitio de internet de MySQLcomUna vez que la instale puede proceder a instalar rails
Rails
Ahora siacute podemos instalar rails Para instalar rails ejecute lo siguiente
$ sudo gem install rails --include-dependencies$ sudo gem install mysql --with-mysql-dir=usrlocalmysql
El paraacutemetro with-mysql-dir soacutelo es necesario si su mysql no instala correctamente (porqueel compilador no encontroacute las libreriacuteas de mysql) Aseguacuterese de que el directorio queutilizaen el paraacutemetro sea el directorio donde instaloacute mysql
Finalmente ejecute el comando rails --version para verificar que su versioacuten de rails fueacuteinstalada correctamente
3 Nuestra primera aplicacioacuten
Creando un Esqueleto
Uno comienza con su aplicacioacuten de Rails creando un esqueleto Crear un esqueleto es muyfacil
$ rails direcciones
create
create appapis
create appcontrollers
create apphelpers
create appmodels
create appviewslayouts
create configenvironments
create components
create db
create doc
create lib
create log
create publicimages
create publicjavascripts
create publicstylesheets
create script
create testfixtures
create testfunctional
create testmocksdevelopment
create testmockstest
create testunit
Facil no Ahora veamos lo que creamos Para esto necesitamos ejecutar un servidor Railspuede ser configurado con apache y FastCGI Pero nuestro esqueleto tambien cuenta conun pequentildeo servidor web Asiacute que lo ejecutamos
$ cd direcciones
$ ruby scriptserver
=gt Rails application started on http00003000
=gt Ctrl-C to shutdown server call with --help for options
[2005-12-06 232744] INFO WEBrick 131[2005-12-06 232744] INFO ruby 182 (2004-12-25) [i386-
mswin32][2005-12-06 232745] INFO WEBrickHTTPServerstart pid=3972
port=3000
Note el puerto que el servidor nos ha dedicado 3000 Este es el puerto que vamos aaccesar utilizando nuestro navegador favorito La direccion es localhost o 127001 Asiacute queen nuestro navegador iremos a http1270013000 Si todo salioacute bien veremos unapantalla de felicitaciones con instrucciones para configurar Apache y pasos siguientes
Ahora que tenemos nuestra aplicacioacuten de rails lista podemos comenzar a ver como estaacutecompuesta nuestra nueva aplicacioacuten
Tu ambiente
Rails 11 tiene un enlace llamado About yourApplications environment que le permite verificarinformacioacuten acerca del ambiente Esta informacioacuten seencuentra en el URL rails_infoproperties
Anatomiacutea de una Aplicacioacuten Rails
Una aplicacioacuten rails se encuentra en el subdirectorio app y se compone de los siguientesdirectorios
apis - las libreriacuteas que su programa requiere fuera de Rails mismocontrollers - Los controladoreshelpers - Los helpersmodels - Los modelos Basicamente las clases que representan los datos que nuestra aplicacioacutenmanipularaacuteviews - Las vistas que son archivos rhtml (como JSP o ASP)
viewslayouts - Los disentildeos Cada controlador tiene su propio disentildeo donde se pondran las cabeceras ypies de paacuteginaconfig - Archivos de configuracioacuten La configuracioacuten de la base de datos se encuentra aquiacutescript - Utileriacuteas para generar coacutedigo y ejecutar el servidorpublic - La raiacutez del directorio virtual Cualquier contenido que usted encuentre aquiacute seraacute publicado en suaplicacioacuten directamente En una nueva aplicacioacuten usted puede encontrar las paacuteginas web para loserrores 404 y 500 las imaacutegenes javascripts estilos etceacuteteratest - Los archivos para las pruebas funcionales y de unidad
Llamando coacutedigo
Seguramente querremos que nuestro directorio tenga una pantalla principalConceptualmente llamaremos a esto el moacutedulo principal Asiacute que lo generamos
$ ruby scriptgenerate controller principal
exists appcontrollers
exists apphelpers
create appviewsprincipal
exists testfunctional
create appcontrollersprincipal_controllerrb
create testfunctionalprincipal_controller_testrb
create apphelpersprincipal_helperrb
Ahora puede usted visitar http1270013000principal para ver
Unknown actionNo action responded to index
El archivo que fue creado se llama controllersprincipal_controllerrb Vamos aponer nuestro coacutedigo ahiacute
class PrincipalController lt ApplicationController
def index
mensaje = Hola a todosltbrgt
[Pedro Pablo Juan]each do |nombre|
mensaje += y a +nombre+ tambienltbrgt
end
render text =gt mensaje
end
end
Cuando visitamos de nuevo vemos nuestro resultado
Hola a todosy a Pedro tambieny a Pablo tambieny a Juan tambien
Finalmente queremos que la raiacutez de esta aplicacioacuten sea tambieacuten nuestro controlador Asiacuteque eliminamos el indexhtml de bienvenida
$ rm publicindexhtml
Si vemos la paacutegina de nuevo ahora diraacute
Routing ErrorRecognition failed for
Esto es porque todaviacutea no le hemos dicho a la aplicacioacuten que nuestro controlador pordefecto es el principal Podemos hacer esto definiendo una ruta Asiacute que modificamos laconfiguracioacuten de rutas en el archivo configroutesrb
mapconnect controller =gt principal
Ahora cuando visitamos la paacutegina vemos el resultado de nuestro controlador principal
Si tiene dudas puede ver la peliacutecula
4 Teoriacutea El Paradigma MVC
MVC son las siglas de Modelo Vista y Controlador Es el patroacuten de disentildeo de software muacuteycomuacuten en programas interactivos orientados a objetos
Bajo el patroacuten de disentildeo de MVC dentro de un programa cada dominio loacutegico de edicioacuten(por ejemplo datos personales cuentas bancarias artiacuteculos noticiosos etceacutetera) necesitatres partes
1 El modelo que es una representacioacuten de objetos del dominio loacutegico En el ejemplo de datos personalesobjetos como Persona Direccioacuten y Teleacutefono
2 La vista que es una o varias piezas de coacutedigo que formatean y muestran todo o parte del modelo en eldispositivo interactivo (tiacutepicamente la pantalla) La vista tiacutepicamente es notificada de cambios en elmodelo
3 El controlador que interpreta la informacioacuten que el usuario provee para hacer cambios en el modelo
La idea es que al aplicar esta separacioacuten se hace posible crear maacutes de una vista para elmismo modelo (digamos una vista abreviada y una vista detallada) y reutilizar el modelo (yel coacutedigo que guarda el modelo de manera permanente) para escribir utileriacuteas relacionadaso incorporar datos del dominio original en programas maacutes grandes
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
era necesario antes con lenguajes compilados como Java Asiacute que maacutes y maacutesdesarrolladores sufriendo los problemas de la tecnologiacutea existente y bajo presion por lanueva metodologiacutea y expectativas comenzaron a buscar alternativas
Durante esta buacutesqueda de alternativas un programador Daneacutes llamado David HeinemeierHansson utilizoacute el lenguaje Ruby para crear una aplicacioacuten llamada Basecamp Las libreriacuteasreutilizables que salieron de este proyecto es lo que hoy conocemos como Ruby on Rails
2 Instalando Rails
Este capiacutetulo explica coacutemo puede usted instalar Rails en su plataforma de desarrollo
Rails es un proyecto de fuente abierta y como tal hay varias maneras de instalarlo yobtenerlo dependiendo de su plataforma y sus necesidades
En este artiacuteculo cubriremos dos maneras de instalar rails
Para principiantesPara usuarios avanzados y ambientes de prueba pre-produccioacuten y produccioacuten
Para Desarrolladores
Si usted es un desarrollador de software la manera maacutes sencilla de utilizar rails es medianteuna solucioacuten todo-en-uno que no afecte su ambiente actual
Windows
Si usted utiliza Windows puede instalar la plataforma Rails completa (incluyendo unaversion de mysql) utilizando proyecto Instant Rails Eacuteste proyecto incluye apache ruby railsy el servidor de mysql todo listo para ejecutarse
Macintosh
Si utiliza Macintosh OS X puede utilizar el proyecto Locomotive Locomotive incluye rubyrails y sqllite (en vez de un servidor de mysql) Si usted tiene un servidor mysql en otramaacutequina locomotive incluye las libreriacuteas de mysql para conectarse
Estos ambientes de desarrollo son adecuados para comenzar a escribir aplicaciones enRails en su propia maacutequina pero no son adecuados para ambientes multiusuario Asiacute queuna vez que usted tenga su programa listo para que otras personas lo utilizen (ya sea enmodo de prueba pre-produccioacuten o produccioacuten) necesitaraacute comprender como instalar unambiente de rails desde cero
Componentes de Rails
Para efectos de instalacioacuten Rails se puede dividir en los siguientes componentes
Lenguaje Ruby El lenguaje en el cual fueacute escrito RailsLas libreriacuteas de Rails Rails se compone de varias libreriacuteas escritas en rubyLa base de datos Una base de datos relacional basada en SQL es indispensable para un sistema dedesarrollo de aplicaciones web moderno La mayor parte de los ejemplos utilizan MySQL
Ruby
Ruby es un lenguaje de programacioacuten interpretado Ruby funciona con cualquier sistemaoperativo moderno pero se siente maacutes en casa en sistemas tipo Unix
La manera en que usted instale ruby depende mucho de su tipo de maacutequina y sus
necesidades Es recomendable que lea todo lo relevante a la plataforma que desee instalarantes de que haga una decisioacuten acerca de coacutemo instalarlo
Ruby en Unix Linux y Mac OS X
Si usted tiene una maacutequina que proviene de la familia Unix (incluyendo Mac OS X) esprobable que ya tenga Ruby incluido Rails requiere de ruby 182 o 184 (pero 183 nofunciona)
Para verificar su versioacuten de ruby ejecute el comando ruby -v Si su versioacuten es adecuadausted puede proceder a la seccioacuten de Gems
Si su versioacuten de ruby no es compatible con rails usted tiene dos opciones
1 Checar si su sistema operativo necesita actualizacioacuten y la versioacuten actualizada incluye un nuevo Ruby En la actualidad los sistemas Linux tienden a tener los maacutes nuevas instalaciones de Ruby en binario En sistemas Solaris algunos usuarios utilizan los paquetes binarios de CSW blastwave para mantenersus sistemas solaris actualizados sin tener que instalar un compilador en la maacutequinaEn Mac OS X depende de su versioacuten del sistema operativo El wiki de RubyGarden tiene informacioacutenacerca de como instalar los binarios
2 Instalar ruby en usrlocal ya sea en binario o compilado
Nota Muchos administradores de sistema Unix (en especial deLinux) prefieren instalar los sistemas de web compilando losarchivos fuente Esto permite el control absoluto de su ambientepero requiere de mayor conocimiento de su sistema asiacute como uncompilador
Si usted decide instalar un sistema ruby desde coacutedigo fuente baje el coacutedigo fuente del sitiooficial de Ruby (en Mac OS X tambieacuten necesita instalar las herramientas XCode quenormalmente vienen incluidos en sus discos del sistema operativo) y utilice la maneraestaacutendar de compilar programas en Unix
$ cd ruby-184-src
$ configure --prefix=usrlocal
$ make$ make install
Despueacutes de instalarlo antildeada el directorio usrlocalrubybin a su variable PATH
Recuerde Si su maacutequina ya incluiacutea ruby el directorio de su nuevacopia de Ruby debe estar antes de los demaacutes directorios en suvariable PATH
Ahora verifique que su instalacioacuten funciona ejecutando ruby -v yverificando la nueva versioacuten
Ruby en Windows
Como mencioneacute anteriormente ruby se encuentra maacutes en casa en un sistema Unix Elproblema principal de Windows es que eacuteste sistema operativo no viene con un compilador -los usuarios de Windows se sienten maacutes comodos con paquetes en binarios y con instalador
Si usted no tiene deseo de aprender a manejar un sistema Unix usted puede utilizar el OneClick Ruby Installer for Windows Este es un instalador que incluye ruby muchas libreriacuteas enbinario y el libro Programming Ruby The Pragmatic Programmers Guide en un archivo de
ayuda Una vez instalado sencillamente ejecute una liacutenea de comando y escriba ruby -vpara verificar su versioacuten
Pero si usted quisiera instalar un subsistema tipo Unix bajo Windows (o si usted estaacuteobligado a usar Windows por su trabajo pero prefiere usar Linux en casa) usted puedeinstalar Cygwin que es una recompilacioacuten de las utileriacuteas GNU bajo windows Durante lainstalacioacuten seleccione Ruby para que se encuentre incluido
Ruby Gems
RubyGems es un manejador de libreriacuteas de Ruby que hace muy sencillo instalar libreriacuteasGems esta a punto de convertirse en parte de ruby Pero si gems no estaacute incluido en suversioacuten de ruby (lo cual puede verificar ejecutando gem -v para ver la versioacuten de gemsinstalada) lo necesita instalar Como usted ya tiene ruby el procedimiento para instalarlo esel mismo para todos los sistemas operativos
cd rubygems
ruby setuprb gem -v
Ahora que tiene ruby gems puede proceder a instalar rails
MySQL
Ahora necesita una base de datos Si usted utiliza Unix es importante instalarla antes deinstalar el paquete mysql de gems porque las libreriacuteas de cliente de mysql son necesarias
Para instalar mysql baje la versioacuten para su plataforma del sitio de internet de MySQLcomUna vez que la instale puede proceder a instalar rails
Rails
Ahora siacute podemos instalar rails Para instalar rails ejecute lo siguiente
$ sudo gem install rails --include-dependencies$ sudo gem install mysql --with-mysql-dir=usrlocalmysql
El paraacutemetro with-mysql-dir soacutelo es necesario si su mysql no instala correctamente (porqueel compilador no encontroacute las libreriacuteas de mysql) Aseguacuterese de que el directorio queutilizaen el paraacutemetro sea el directorio donde instaloacute mysql
Finalmente ejecute el comando rails --version para verificar que su versioacuten de rails fueacuteinstalada correctamente
3 Nuestra primera aplicacioacuten
Creando un Esqueleto
Uno comienza con su aplicacioacuten de Rails creando un esqueleto Crear un esqueleto es muyfacil
$ rails direcciones
create
create appapis
create appcontrollers
create apphelpers
create appmodels
create appviewslayouts
create configenvironments
create components
create db
create doc
create lib
create log
create publicimages
create publicjavascripts
create publicstylesheets
create script
create testfixtures
create testfunctional
create testmocksdevelopment
create testmockstest
create testunit
Facil no Ahora veamos lo que creamos Para esto necesitamos ejecutar un servidor Railspuede ser configurado con apache y FastCGI Pero nuestro esqueleto tambien cuenta conun pequentildeo servidor web Asiacute que lo ejecutamos
$ cd direcciones
$ ruby scriptserver
=gt Rails application started on http00003000
=gt Ctrl-C to shutdown server call with --help for options
[2005-12-06 232744] INFO WEBrick 131[2005-12-06 232744] INFO ruby 182 (2004-12-25) [i386-
mswin32][2005-12-06 232745] INFO WEBrickHTTPServerstart pid=3972
port=3000
Note el puerto que el servidor nos ha dedicado 3000 Este es el puerto que vamos aaccesar utilizando nuestro navegador favorito La direccion es localhost o 127001 Asiacute queen nuestro navegador iremos a http1270013000 Si todo salioacute bien veremos unapantalla de felicitaciones con instrucciones para configurar Apache y pasos siguientes
Ahora que tenemos nuestra aplicacioacuten de rails lista podemos comenzar a ver como estaacutecompuesta nuestra nueva aplicacioacuten
Tu ambiente
Rails 11 tiene un enlace llamado About yourApplications environment que le permite verificarinformacioacuten acerca del ambiente Esta informacioacuten seencuentra en el URL rails_infoproperties
Anatomiacutea de una Aplicacioacuten Rails
Una aplicacioacuten rails se encuentra en el subdirectorio app y se compone de los siguientesdirectorios
apis - las libreriacuteas que su programa requiere fuera de Rails mismocontrollers - Los controladoreshelpers - Los helpersmodels - Los modelos Basicamente las clases que representan los datos que nuestra aplicacioacutenmanipularaacuteviews - Las vistas que son archivos rhtml (como JSP o ASP)
viewslayouts - Los disentildeos Cada controlador tiene su propio disentildeo donde se pondran las cabeceras ypies de paacuteginaconfig - Archivos de configuracioacuten La configuracioacuten de la base de datos se encuentra aquiacutescript - Utileriacuteas para generar coacutedigo y ejecutar el servidorpublic - La raiacutez del directorio virtual Cualquier contenido que usted encuentre aquiacute seraacute publicado en suaplicacioacuten directamente En una nueva aplicacioacuten usted puede encontrar las paacuteginas web para loserrores 404 y 500 las imaacutegenes javascripts estilos etceacuteteratest - Los archivos para las pruebas funcionales y de unidad
Llamando coacutedigo
Seguramente querremos que nuestro directorio tenga una pantalla principalConceptualmente llamaremos a esto el moacutedulo principal Asiacute que lo generamos
$ ruby scriptgenerate controller principal
exists appcontrollers
exists apphelpers
create appviewsprincipal
exists testfunctional
create appcontrollersprincipal_controllerrb
create testfunctionalprincipal_controller_testrb
create apphelpersprincipal_helperrb
Ahora puede usted visitar http1270013000principal para ver
Unknown actionNo action responded to index
El archivo que fue creado se llama controllersprincipal_controllerrb Vamos aponer nuestro coacutedigo ahiacute
class PrincipalController lt ApplicationController
def index
mensaje = Hola a todosltbrgt
[Pedro Pablo Juan]each do |nombre|
mensaje += y a +nombre+ tambienltbrgt
end
render text =gt mensaje
end
end
Cuando visitamos de nuevo vemos nuestro resultado
Hola a todosy a Pedro tambieny a Pablo tambieny a Juan tambien
Finalmente queremos que la raiacutez de esta aplicacioacuten sea tambieacuten nuestro controlador Asiacuteque eliminamos el indexhtml de bienvenida
$ rm publicindexhtml
Si vemos la paacutegina de nuevo ahora diraacute
Routing ErrorRecognition failed for
Esto es porque todaviacutea no le hemos dicho a la aplicacioacuten que nuestro controlador pordefecto es el principal Podemos hacer esto definiendo una ruta Asiacute que modificamos laconfiguracioacuten de rutas en el archivo configroutesrb
mapconnect controller =gt principal
Ahora cuando visitamos la paacutegina vemos el resultado de nuestro controlador principal
Si tiene dudas puede ver la peliacutecula
4 Teoriacutea El Paradigma MVC
MVC son las siglas de Modelo Vista y Controlador Es el patroacuten de disentildeo de software muacuteycomuacuten en programas interactivos orientados a objetos
Bajo el patroacuten de disentildeo de MVC dentro de un programa cada dominio loacutegico de edicioacuten(por ejemplo datos personales cuentas bancarias artiacuteculos noticiosos etceacutetera) necesitatres partes
1 El modelo que es una representacioacuten de objetos del dominio loacutegico En el ejemplo de datos personalesobjetos como Persona Direccioacuten y Teleacutefono
2 La vista que es una o varias piezas de coacutedigo que formatean y muestran todo o parte del modelo en eldispositivo interactivo (tiacutepicamente la pantalla) La vista tiacutepicamente es notificada de cambios en elmodelo
3 El controlador que interpreta la informacioacuten que el usuario provee para hacer cambios en el modelo
La idea es que al aplicar esta separacioacuten se hace posible crear maacutes de una vista para elmismo modelo (digamos una vista abreviada y una vista detallada) y reutilizar el modelo (yel coacutedigo que guarda el modelo de manera permanente) para escribir utileriacuteas relacionadaso incorporar datos del dominio original en programas maacutes grandes
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
2 Instalando Rails
Este capiacutetulo explica coacutemo puede usted instalar Rails en su plataforma de desarrollo
Rails es un proyecto de fuente abierta y como tal hay varias maneras de instalarlo yobtenerlo dependiendo de su plataforma y sus necesidades
En este artiacuteculo cubriremos dos maneras de instalar rails
Para principiantesPara usuarios avanzados y ambientes de prueba pre-produccioacuten y produccioacuten
Para Desarrolladores
Si usted es un desarrollador de software la manera maacutes sencilla de utilizar rails es medianteuna solucioacuten todo-en-uno que no afecte su ambiente actual
Windows
Si usted utiliza Windows puede instalar la plataforma Rails completa (incluyendo unaversion de mysql) utilizando proyecto Instant Rails Eacuteste proyecto incluye apache ruby railsy el servidor de mysql todo listo para ejecutarse
Macintosh
Si utiliza Macintosh OS X puede utilizar el proyecto Locomotive Locomotive incluye rubyrails y sqllite (en vez de un servidor de mysql) Si usted tiene un servidor mysql en otramaacutequina locomotive incluye las libreriacuteas de mysql para conectarse
Estos ambientes de desarrollo son adecuados para comenzar a escribir aplicaciones enRails en su propia maacutequina pero no son adecuados para ambientes multiusuario Asiacute queuna vez que usted tenga su programa listo para que otras personas lo utilizen (ya sea enmodo de prueba pre-produccioacuten o produccioacuten) necesitaraacute comprender como instalar unambiente de rails desde cero
Componentes de Rails
Para efectos de instalacioacuten Rails se puede dividir en los siguientes componentes
Lenguaje Ruby El lenguaje en el cual fueacute escrito RailsLas libreriacuteas de Rails Rails se compone de varias libreriacuteas escritas en rubyLa base de datos Una base de datos relacional basada en SQL es indispensable para un sistema dedesarrollo de aplicaciones web moderno La mayor parte de los ejemplos utilizan MySQL
Ruby
Ruby es un lenguaje de programacioacuten interpretado Ruby funciona con cualquier sistemaoperativo moderno pero se siente maacutes en casa en sistemas tipo Unix
La manera en que usted instale ruby depende mucho de su tipo de maacutequina y sus
necesidades Es recomendable que lea todo lo relevante a la plataforma que desee instalarantes de que haga una decisioacuten acerca de coacutemo instalarlo
Ruby en Unix Linux y Mac OS X
Si usted tiene una maacutequina que proviene de la familia Unix (incluyendo Mac OS X) esprobable que ya tenga Ruby incluido Rails requiere de ruby 182 o 184 (pero 183 nofunciona)
Para verificar su versioacuten de ruby ejecute el comando ruby -v Si su versioacuten es adecuadausted puede proceder a la seccioacuten de Gems
Si su versioacuten de ruby no es compatible con rails usted tiene dos opciones
1 Checar si su sistema operativo necesita actualizacioacuten y la versioacuten actualizada incluye un nuevo Ruby En la actualidad los sistemas Linux tienden a tener los maacutes nuevas instalaciones de Ruby en binario En sistemas Solaris algunos usuarios utilizan los paquetes binarios de CSW blastwave para mantenersus sistemas solaris actualizados sin tener que instalar un compilador en la maacutequinaEn Mac OS X depende de su versioacuten del sistema operativo El wiki de RubyGarden tiene informacioacutenacerca de como instalar los binarios
2 Instalar ruby en usrlocal ya sea en binario o compilado
Nota Muchos administradores de sistema Unix (en especial deLinux) prefieren instalar los sistemas de web compilando losarchivos fuente Esto permite el control absoluto de su ambientepero requiere de mayor conocimiento de su sistema asiacute como uncompilador
Si usted decide instalar un sistema ruby desde coacutedigo fuente baje el coacutedigo fuente del sitiooficial de Ruby (en Mac OS X tambieacuten necesita instalar las herramientas XCode quenormalmente vienen incluidos en sus discos del sistema operativo) y utilice la maneraestaacutendar de compilar programas en Unix
$ cd ruby-184-src
$ configure --prefix=usrlocal
$ make$ make install
Despueacutes de instalarlo antildeada el directorio usrlocalrubybin a su variable PATH
Recuerde Si su maacutequina ya incluiacutea ruby el directorio de su nuevacopia de Ruby debe estar antes de los demaacutes directorios en suvariable PATH
Ahora verifique que su instalacioacuten funciona ejecutando ruby -v yverificando la nueva versioacuten
Ruby en Windows
Como mencioneacute anteriormente ruby se encuentra maacutes en casa en un sistema Unix Elproblema principal de Windows es que eacuteste sistema operativo no viene con un compilador -los usuarios de Windows se sienten maacutes comodos con paquetes en binarios y con instalador
Si usted no tiene deseo de aprender a manejar un sistema Unix usted puede utilizar el OneClick Ruby Installer for Windows Este es un instalador que incluye ruby muchas libreriacuteas enbinario y el libro Programming Ruby The Pragmatic Programmers Guide en un archivo de
ayuda Una vez instalado sencillamente ejecute una liacutenea de comando y escriba ruby -vpara verificar su versioacuten
Pero si usted quisiera instalar un subsistema tipo Unix bajo Windows (o si usted estaacuteobligado a usar Windows por su trabajo pero prefiere usar Linux en casa) usted puedeinstalar Cygwin que es una recompilacioacuten de las utileriacuteas GNU bajo windows Durante lainstalacioacuten seleccione Ruby para que se encuentre incluido
Ruby Gems
RubyGems es un manejador de libreriacuteas de Ruby que hace muy sencillo instalar libreriacuteasGems esta a punto de convertirse en parte de ruby Pero si gems no estaacute incluido en suversioacuten de ruby (lo cual puede verificar ejecutando gem -v para ver la versioacuten de gemsinstalada) lo necesita instalar Como usted ya tiene ruby el procedimiento para instalarlo esel mismo para todos los sistemas operativos
cd rubygems
ruby setuprb gem -v
Ahora que tiene ruby gems puede proceder a instalar rails
MySQL
Ahora necesita una base de datos Si usted utiliza Unix es importante instalarla antes deinstalar el paquete mysql de gems porque las libreriacuteas de cliente de mysql son necesarias
Para instalar mysql baje la versioacuten para su plataforma del sitio de internet de MySQLcomUna vez que la instale puede proceder a instalar rails
Rails
Ahora siacute podemos instalar rails Para instalar rails ejecute lo siguiente
$ sudo gem install rails --include-dependencies$ sudo gem install mysql --with-mysql-dir=usrlocalmysql
El paraacutemetro with-mysql-dir soacutelo es necesario si su mysql no instala correctamente (porqueel compilador no encontroacute las libreriacuteas de mysql) Aseguacuterese de que el directorio queutilizaen el paraacutemetro sea el directorio donde instaloacute mysql
Finalmente ejecute el comando rails --version para verificar que su versioacuten de rails fueacuteinstalada correctamente
3 Nuestra primera aplicacioacuten
Creando un Esqueleto
Uno comienza con su aplicacioacuten de Rails creando un esqueleto Crear un esqueleto es muyfacil
$ rails direcciones
create
create appapis
create appcontrollers
create apphelpers
create appmodels
create appviewslayouts
create configenvironments
create components
create db
create doc
create lib
create log
create publicimages
create publicjavascripts
create publicstylesheets
create script
create testfixtures
create testfunctional
create testmocksdevelopment
create testmockstest
create testunit
Facil no Ahora veamos lo que creamos Para esto necesitamos ejecutar un servidor Railspuede ser configurado con apache y FastCGI Pero nuestro esqueleto tambien cuenta conun pequentildeo servidor web Asiacute que lo ejecutamos
$ cd direcciones
$ ruby scriptserver
=gt Rails application started on http00003000
=gt Ctrl-C to shutdown server call with --help for options
[2005-12-06 232744] INFO WEBrick 131[2005-12-06 232744] INFO ruby 182 (2004-12-25) [i386-
mswin32][2005-12-06 232745] INFO WEBrickHTTPServerstart pid=3972
port=3000
Note el puerto que el servidor nos ha dedicado 3000 Este es el puerto que vamos aaccesar utilizando nuestro navegador favorito La direccion es localhost o 127001 Asiacute queen nuestro navegador iremos a http1270013000 Si todo salioacute bien veremos unapantalla de felicitaciones con instrucciones para configurar Apache y pasos siguientes
Ahora que tenemos nuestra aplicacioacuten de rails lista podemos comenzar a ver como estaacutecompuesta nuestra nueva aplicacioacuten
Tu ambiente
Rails 11 tiene un enlace llamado About yourApplications environment que le permite verificarinformacioacuten acerca del ambiente Esta informacioacuten seencuentra en el URL rails_infoproperties
Anatomiacutea de una Aplicacioacuten Rails
Una aplicacioacuten rails se encuentra en el subdirectorio app y se compone de los siguientesdirectorios
apis - las libreriacuteas que su programa requiere fuera de Rails mismocontrollers - Los controladoreshelpers - Los helpersmodels - Los modelos Basicamente las clases que representan los datos que nuestra aplicacioacutenmanipularaacuteviews - Las vistas que son archivos rhtml (como JSP o ASP)
viewslayouts - Los disentildeos Cada controlador tiene su propio disentildeo donde se pondran las cabeceras ypies de paacuteginaconfig - Archivos de configuracioacuten La configuracioacuten de la base de datos se encuentra aquiacutescript - Utileriacuteas para generar coacutedigo y ejecutar el servidorpublic - La raiacutez del directorio virtual Cualquier contenido que usted encuentre aquiacute seraacute publicado en suaplicacioacuten directamente En una nueva aplicacioacuten usted puede encontrar las paacuteginas web para loserrores 404 y 500 las imaacutegenes javascripts estilos etceacuteteratest - Los archivos para las pruebas funcionales y de unidad
Llamando coacutedigo
Seguramente querremos que nuestro directorio tenga una pantalla principalConceptualmente llamaremos a esto el moacutedulo principal Asiacute que lo generamos
$ ruby scriptgenerate controller principal
exists appcontrollers
exists apphelpers
create appviewsprincipal
exists testfunctional
create appcontrollersprincipal_controllerrb
create testfunctionalprincipal_controller_testrb
create apphelpersprincipal_helperrb
Ahora puede usted visitar http1270013000principal para ver
Unknown actionNo action responded to index
El archivo que fue creado se llama controllersprincipal_controllerrb Vamos aponer nuestro coacutedigo ahiacute
class PrincipalController lt ApplicationController
def index
mensaje = Hola a todosltbrgt
[Pedro Pablo Juan]each do |nombre|
mensaje += y a +nombre+ tambienltbrgt
end
render text =gt mensaje
end
end
Cuando visitamos de nuevo vemos nuestro resultado
Hola a todosy a Pedro tambieny a Pablo tambieny a Juan tambien
Finalmente queremos que la raiacutez de esta aplicacioacuten sea tambieacuten nuestro controlador Asiacuteque eliminamos el indexhtml de bienvenida
$ rm publicindexhtml
Si vemos la paacutegina de nuevo ahora diraacute
Routing ErrorRecognition failed for
Esto es porque todaviacutea no le hemos dicho a la aplicacioacuten que nuestro controlador pordefecto es el principal Podemos hacer esto definiendo una ruta Asiacute que modificamos laconfiguracioacuten de rutas en el archivo configroutesrb
mapconnect controller =gt principal
Ahora cuando visitamos la paacutegina vemos el resultado de nuestro controlador principal
Si tiene dudas puede ver la peliacutecula
4 Teoriacutea El Paradigma MVC
MVC son las siglas de Modelo Vista y Controlador Es el patroacuten de disentildeo de software muacuteycomuacuten en programas interactivos orientados a objetos
Bajo el patroacuten de disentildeo de MVC dentro de un programa cada dominio loacutegico de edicioacuten(por ejemplo datos personales cuentas bancarias artiacuteculos noticiosos etceacutetera) necesitatres partes
1 El modelo que es una representacioacuten de objetos del dominio loacutegico En el ejemplo de datos personalesobjetos como Persona Direccioacuten y Teleacutefono
2 La vista que es una o varias piezas de coacutedigo que formatean y muestran todo o parte del modelo en eldispositivo interactivo (tiacutepicamente la pantalla) La vista tiacutepicamente es notificada de cambios en elmodelo
3 El controlador que interpreta la informacioacuten que el usuario provee para hacer cambios en el modelo
La idea es que al aplicar esta separacioacuten se hace posible crear maacutes de una vista para elmismo modelo (digamos una vista abreviada y una vista detallada) y reutilizar el modelo (yel coacutedigo que guarda el modelo de manera permanente) para escribir utileriacuteas relacionadaso incorporar datos del dominio original en programas maacutes grandes
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
necesidades Es recomendable que lea todo lo relevante a la plataforma que desee instalarantes de que haga una decisioacuten acerca de coacutemo instalarlo
Ruby en Unix Linux y Mac OS X
Si usted tiene una maacutequina que proviene de la familia Unix (incluyendo Mac OS X) esprobable que ya tenga Ruby incluido Rails requiere de ruby 182 o 184 (pero 183 nofunciona)
Para verificar su versioacuten de ruby ejecute el comando ruby -v Si su versioacuten es adecuadausted puede proceder a la seccioacuten de Gems
Si su versioacuten de ruby no es compatible con rails usted tiene dos opciones
1 Checar si su sistema operativo necesita actualizacioacuten y la versioacuten actualizada incluye un nuevo Ruby En la actualidad los sistemas Linux tienden a tener los maacutes nuevas instalaciones de Ruby en binario En sistemas Solaris algunos usuarios utilizan los paquetes binarios de CSW blastwave para mantenersus sistemas solaris actualizados sin tener que instalar un compilador en la maacutequinaEn Mac OS X depende de su versioacuten del sistema operativo El wiki de RubyGarden tiene informacioacutenacerca de como instalar los binarios
2 Instalar ruby en usrlocal ya sea en binario o compilado
Nota Muchos administradores de sistema Unix (en especial deLinux) prefieren instalar los sistemas de web compilando losarchivos fuente Esto permite el control absoluto de su ambientepero requiere de mayor conocimiento de su sistema asiacute como uncompilador
Si usted decide instalar un sistema ruby desde coacutedigo fuente baje el coacutedigo fuente del sitiooficial de Ruby (en Mac OS X tambieacuten necesita instalar las herramientas XCode quenormalmente vienen incluidos en sus discos del sistema operativo) y utilice la maneraestaacutendar de compilar programas en Unix
$ cd ruby-184-src
$ configure --prefix=usrlocal
$ make$ make install
Despueacutes de instalarlo antildeada el directorio usrlocalrubybin a su variable PATH
Recuerde Si su maacutequina ya incluiacutea ruby el directorio de su nuevacopia de Ruby debe estar antes de los demaacutes directorios en suvariable PATH
Ahora verifique que su instalacioacuten funciona ejecutando ruby -v yverificando la nueva versioacuten
Ruby en Windows
Como mencioneacute anteriormente ruby se encuentra maacutes en casa en un sistema Unix Elproblema principal de Windows es que eacuteste sistema operativo no viene con un compilador -los usuarios de Windows se sienten maacutes comodos con paquetes en binarios y con instalador
Si usted no tiene deseo de aprender a manejar un sistema Unix usted puede utilizar el OneClick Ruby Installer for Windows Este es un instalador que incluye ruby muchas libreriacuteas enbinario y el libro Programming Ruby The Pragmatic Programmers Guide en un archivo de
ayuda Una vez instalado sencillamente ejecute una liacutenea de comando y escriba ruby -vpara verificar su versioacuten
Pero si usted quisiera instalar un subsistema tipo Unix bajo Windows (o si usted estaacuteobligado a usar Windows por su trabajo pero prefiere usar Linux en casa) usted puedeinstalar Cygwin que es una recompilacioacuten de las utileriacuteas GNU bajo windows Durante lainstalacioacuten seleccione Ruby para que se encuentre incluido
Ruby Gems
RubyGems es un manejador de libreriacuteas de Ruby que hace muy sencillo instalar libreriacuteasGems esta a punto de convertirse en parte de ruby Pero si gems no estaacute incluido en suversioacuten de ruby (lo cual puede verificar ejecutando gem -v para ver la versioacuten de gemsinstalada) lo necesita instalar Como usted ya tiene ruby el procedimiento para instalarlo esel mismo para todos los sistemas operativos
cd rubygems
ruby setuprb gem -v
Ahora que tiene ruby gems puede proceder a instalar rails
MySQL
Ahora necesita una base de datos Si usted utiliza Unix es importante instalarla antes deinstalar el paquete mysql de gems porque las libreriacuteas de cliente de mysql son necesarias
Para instalar mysql baje la versioacuten para su plataforma del sitio de internet de MySQLcomUna vez que la instale puede proceder a instalar rails
Rails
Ahora siacute podemos instalar rails Para instalar rails ejecute lo siguiente
$ sudo gem install rails --include-dependencies$ sudo gem install mysql --with-mysql-dir=usrlocalmysql
El paraacutemetro with-mysql-dir soacutelo es necesario si su mysql no instala correctamente (porqueel compilador no encontroacute las libreriacuteas de mysql) Aseguacuterese de que el directorio queutilizaen el paraacutemetro sea el directorio donde instaloacute mysql
Finalmente ejecute el comando rails --version para verificar que su versioacuten de rails fueacuteinstalada correctamente
3 Nuestra primera aplicacioacuten
Creando un Esqueleto
Uno comienza con su aplicacioacuten de Rails creando un esqueleto Crear un esqueleto es muyfacil
$ rails direcciones
create
create appapis
create appcontrollers
create apphelpers
create appmodels
create appviewslayouts
create configenvironments
create components
create db
create doc
create lib
create log
create publicimages
create publicjavascripts
create publicstylesheets
create script
create testfixtures
create testfunctional
create testmocksdevelopment
create testmockstest
create testunit
Facil no Ahora veamos lo que creamos Para esto necesitamos ejecutar un servidor Railspuede ser configurado con apache y FastCGI Pero nuestro esqueleto tambien cuenta conun pequentildeo servidor web Asiacute que lo ejecutamos
$ cd direcciones
$ ruby scriptserver
=gt Rails application started on http00003000
=gt Ctrl-C to shutdown server call with --help for options
[2005-12-06 232744] INFO WEBrick 131[2005-12-06 232744] INFO ruby 182 (2004-12-25) [i386-
mswin32][2005-12-06 232745] INFO WEBrickHTTPServerstart pid=3972
port=3000
Note el puerto que el servidor nos ha dedicado 3000 Este es el puerto que vamos aaccesar utilizando nuestro navegador favorito La direccion es localhost o 127001 Asiacute queen nuestro navegador iremos a http1270013000 Si todo salioacute bien veremos unapantalla de felicitaciones con instrucciones para configurar Apache y pasos siguientes
Ahora que tenemos nuestra aplicacioacuten de rails lista podemos comenzar a ver como estaacutecompuesta nuestra nueva aplicacioacuten
Tu ambiente
Rails 11 tiene un enlace llamado About yourApplications environment que le permite verificarinformacioacuten acerca del ambiente Esta informacioacuten seencuentra en el URL rails_infoproperties
Anatomiacutea de una Aplicacioacuten Rails
Una aplicacioacuten rails se encuentra en el subdirectorio app y se compone de los siguientesdirectorios
apis - las libreriacuteas que su programa requiere fuera de Rails mismocontrollers - Los controladoreshelpers - Los helpersmodels - Los modelos Basicamente las clases que representan los datos que nuestra aplicacioacutenmanipularaacuteviews - Las vistas que son archivos rhtml (como JSP o ASP)
viewslayouts - Los disentildeos Cada controlador tiene su propio disentildeo donde se pondran las cabeceras ypies de paacuteginaconfig - Archivos de configuracioacuten La configuracioacuten de la base de datos se encuentra aquiacutescript - Utileriacuteas para generar coacutedigo y ejecutar el servidorpublic - La raiacutez del directorio virtual Cualquier contenido que usted encuentre aquiacute seraacute publicado en suaplicacioacuten directamente En una nueva aplicacioacuten usted puede encontrar las paacuteginas web para loserrores 404 y 500 las imaacutegenes javascripts estilos etceacuteteratest - Los archivos para las pruebas funcionales y de unidad
Llamando coacutedigo
Seguramente querremos que nuestro directorio tenga una pantalla principalConceptualmente llamaremos a esto el moacutedulo principal Asiacute que lo generamos
$ ruby scriptgenerate controller principal
exists appcontrollers
exists apphelpers
create appviewsprincipal
exists testfunctional
create appcontrollersprincipal_controllerrb
create testfunctionalprincipal_controller_testrb
create apphelpersprincipal_helperrb
Ahora puede usted visitar http1270013000principal para ver
Unknown actionNo action responded to index
El archivo que fue creado se llama controllersprincipal_controllerrb Vamos aponer nuestro coacutedigo ahiacute
class PrincipalController lt ApplicationController
def index
mensaje = Hola a todosltbrgt
[Pedro Pablo Juan]each do |nombre|
mensaje += y a +nombre+ tambienltbrgt
end
render text =gt mensaje
end
end
Cuando visitamos de nuevo vemos nuestro resultado
Hola a todosy a Pedro tambieny a Pablo tambieny a Juan tambien
Finalmente queremos que la raiacutez de esta aplicacioacuten sea tambieacuten nuestro controlador Asiacuteque eliminamos el indexhtml de bienvenida
$ rm publicindexhtml
Si vemos la paacutegina de nuevo ahora diraacute
Routing ErrorRecognition failed for
Esto es porque todaviacutea no le hemos dicho a la aplicacioacuten que nuestro controlador pordefecto es el principal Podemos hacer esto definiendo una ruta Asiacute que modificamos laconfiguracioacuten de rutas en el archivo configroutesrb
mapconnect controller =gt principal
Ahora cuando visitamos la paacutegina vemos el resultado de nuestro controlador principal
Si tiene dudas puede ver la peliacutecula
4 Teoriacutea El Paradigma MVC
MVC son las siglas de Modelo Vista y Controlador Es el patroacuten de disentildeo de software muacuteycomuacuten en programas interactivos orientados a objetos
Bajo el patroacuten de disentildeo de MVC dentro de un programa cada dominio loacutegico de edicioacuten(por ejemplo datos personales cuentas bancarias artiacuteculos noticiosos etceacutetera) necesitatres partes
1 El modelo que es una representacioacuten de objetos del dominio loacutegico En el ejemplo de datos personalesobjetos como Persona Direccioacuten y Teleacutefono
2 La vista que es una o varias piezas de coacutedigo que formatean y muestran todo o parte del modelo en eldispositivo interactivo (tiacutepicamente la pantalla) La vista tiacutepicamente es notificada de cambios en elmodelo
3 El controlador que interpreta la informacioacuten que el usuario provee para hacer cambios en el modelo
La idea es que al aplicar esta separacioacuten se hace posible crear maacutes de una vista para elmismo modelo (digamos una vista abreviada y una vista detallada) y reutilizar el modelo (yel coacutedigo que guarda el modelo de manera permanente) para escribir utileriacuteas relacionadaso incorporar datos del dominio original en programas maacutes grandes
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
ayuda Una vez instalado sencillamente ejecute una liacutenea de comando y escriba ruby -vpara verificar su versioacuten
Pero si usted quisiera instalar un subsistema tipo Unix bajo Windows (o si usted estaacuteobligado a usar Windows por su trabajo pero prefiere usar Linux en casa) usted puedeinstalar Cygwin que es una recompilacioacuten de las utileriacuteas GNU bajo windows Durante lainstalacioacuten seleccione Ruby para que se encuentre incluido
Ruby Gems
RubyGems es un manejador de libreriacuteas de Ruby que hace muy sencillo instalar libreriacuteasGems esta a punto de convertirse en parte de ruby Pero si gems no estaacute incluido en suversioacuten de ruby (lo cual puede verificar ejecutando gem -v para ver la versioacuten de gemsinstalada) lo necesita instalar Como usted ya tiene ruby el procedimiento para instalarlo esel mismo para todos los sistemas operativos
cd rubygems
ruby setuprb gem -v
Ahora que tiene ruby gems puede proceder a instalar rails
MySQL
Ahora necesita una base de datos Si usted utiliza Unix es importante instalarla antes deinstalar el paquete mysql de gems porque las libreriacuteas de cliente de mysql son necesarias
Para instalar mysql baje la versioacuten para su plataforma del sitio de internet de MySQLcomUna vez que la instale puede proceder a instalar rails
Rails
Ahora siacute podemos instalar rails Para instalar rails ejecute lo siguiente
$ sudo gem install rails --include-dependencies$ sudo gem install mysql --with-mysql-dir=usrlocalmysql
El paraacutemetro with-mysql-dir soacutelo es necesario si su mysql no instala correctamente (porqueel compilador no encontroacute las libreriacuteas de mysql) Aseguacuterese de que el directorio queutilizaen el paraacutemetro sea el directorio donde instaloacute mysql
Finalmente ejecute el comando rails --version para verificar que su versioacuten de rails fueacuteinstalada correctamente
3 Nuestra primera aplicacioacuten
Creando un Esqueleto
Uno comienza con su aplicacioacuten de Rails creando un esqueleto Crear un esqueleto es muyfacil
$ rails direcciones
create
create appapis
create appcontrollers
create apphelpers
create appmodels
create appviewslayouts
create configenvironments
create components
create db
create doc
create lib
create log
create publicimages
create publicjavascripts
create publicstylesheets
create script
create testfixtures
create testfunctional
create testmocksdevelopment
create testmockstest
create testunit
Facil no Ahora veamos lo que creamos Para esto necesitamos ejecutar un servidor Railspuede ser configurado con apache y FastCGI Pero nuestro esqueleto tambien cuenta conun pequentildeo servidor web Asiacute que lo ejecutamos
$ cd direcciones
$ ruby scriptserver
=gt Rails application started on http00003000
=gt Ctrl-C to shutdown server call with --help for options
[2005-12-06 232744] INFO WEBrick 131[2005-12-06 232744] INFO ruby 182 (2004-12-25) [i386-
mswin32][2005-12-06 232745] INFO WEBrickHTTPServerstart pid=3972
port=3000
Note el puerto que el servidor nos ha dedicado 3000 Este es el puerto que vamos aaccesar utilizando nuestro navegador favorito La direccion es localhost o 127001 Asiacute queen nuestro navegador iremos a http1270013000 Si todo salioacute bien veremos unapantalla de felicitaciones con instrucciones para configurar Apache y pasos siguientes
Ahora que tenemos nuestra aplicacioacuten de rails lista podemos comenzar a ver como estaacutecompuesta nuestra nueva aplicacioacuten
Tu ambiente
Rails 11 tiene un enlace llamado About yourApplications environment que le permite verificarinformacioacuten acerca del ambiente Esta informacioacuten seencuentra en el URL rails_infoproperties
Anatomiacutea de una Aplicacioacuten Rails
Una aplicacioacuten rails se encuentra en el subdirectorio app y se compone de los siguientesdirectorios
apis - las libreriacuteas que su programa requiere fuera de Rails mismocontrollers - Los controladoreshelpers - Los helpersmodels - Los modelos Basicamente las clases que representan los datos que nuestra aplicacioacutenmanipularaacuteviews - Las vistas que son archivos rhtml (como JSP o ASP)
viewslayouts - Los disentildeos Cada controlador tiene su propio disentildeo donde se pondran las cabeceras ypies de paacuteginaconfig - Archivos de configuracioacuten La configuracioacuten de la base de datos se encuentra aquiacutescript - Utileriacuteas para generar coacutedigo y ejecutar el servidorpublic - La raiacutez del directorio virtual Cualquier contenido que usted encuentre aquiacute seraacute publicado en suaplicacioacuten directamente En una nueva aplicacioacuten usted puede encontrar las paacuteginas web para loserrores 404 y 500 las imaacutegenes javascripts estilos etceacuteteratest - Los archivos para las pruebas funcionales y de unidad
Llamando coacutedigo
Seguramente querremos que nuestro directorio tenga una pantalla principalConceptualmente llamaremos a esto el moacutedulo principal Asiacute que lo generamos
$ ruby scriptgenerate controller principal
exists appcontrollers
exists apphelpers
create appviewsprincipal
exists testfunctional
create appcontrollersprincipal_controllerrb
create testfunctionalprincipal_controller_testrb
create apphelpersprincipal_helperrb
Ahora puede usted visitar http1270013000principal para ver
Unknown actionNo action responded to index
El archivo que fue creado se llama controllersprincipal_controllerrb Vamos aponer nuestro coacutedigo ahiacute
class PrincipalController lt ApplicationController
def index
mensaje = Hola a todosltbrgt
[Pedro Pablo Juan]each do |nombre|
mensaje += y a +nombre+ tambienltbrgt
end
render text =gt mensaje
end
end
Cuando visitamos de nuevo vemos nuestro resultado
Hola a todosy a Pedro tambieny a Pablo tambieny a Juan tambien
Finalmente queremos que la raiacutez de esta aplicacioacuten sea tambieacuten nuestro controlador Asiacuteque eliminamos el indexhtml de bienvenida
$ rm publicindexhtml
Si vemos la paacutegina de nuevo ahora diraacute
Routing ErrorRecognition failed for
Esto es porque todaviacutea no le hemos dicho a la aplicacioacuten que nuestro controlador pordefecto es el principal Podemos hacer esto definiendo una ruta Asiacute que modificamos laconfiguracioacuten de rutas en el archivo configroutesrb
mapconnect controller =gt principal
Ahora cuando visitamos la paacutegina vemos el resultado de nuestro controlador principal
Si tiene dudas puede ver la peliacutecula
4 Teoriacutea El Paradigma MVC
MVC son las siglas de Modelo Vista y Controlador Es el patroacuten de disentildeo de software muacuteycomuacuten en programas interactivos orientados a objetos
Bajo el patroacuten de disentildeo de MVC dentro de un programa cada dominio loacutegico de edicioacuten(por ejemplo datos personales cuentas bancarias artiacuteculos noticiosos etceacutetera) necesitatres partes
1 El modelo que es una representacioacuten de objetos del dominio loacutegico En el ejemplo de datos personalesobjetos como Persona Direccioacuten y Teleacutefono
2 La vista que es una o varias piezas de coacutedigo que formatean y muestran todo o parte del modelo en eldispositivo interactivo (tiacutepicamente la pantalla) La vista tiacutepicamente es notificada de cambios en elmodelo
3 El controlador que interpreta la informacioacuten que el usuario provee para hacer cambios en el modelo
La idea es que al aplicar esta separacioacuten se hace posible crear maacutes de una vista para elmismo modelo (digamos una vista abreviada y una vista detallada) y reutilizar el modelo (yel coacutedigo que guarda el modelo de manera permanente) para escribir utileriacuteas relacionadaso incorporar datos del dominio original en programas maacutes grandes
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
3 Nuestra primera aplicacioacuten
Creando un Esqueleto
Uno comienza con su aplicacioacuten de Rails creando un esqueleto Crear un esqueleto es muyfacil
$ rails direcciones
create
create appapis
create appcontrollers
create apphelpers
create appmodels
create appviewslayouts
create configenvironments
create components
create db
create doc
create lib
create log
create publicimages
create publicjavascripts
create publicstylesheets
create script
create testfixtures
create testfunctional
create testmocksdevelopment
create testmockstest
create testunit
Facil no Ahora veamos lo que creamos Para esto necesitamos ejecutar un servidor Railspuede ser configurado con apache y FastCGI Pero nuestro esqueleto tambien cuenta conun pequentildeo servidor web Asiacute que lo ejecutamos
$ cd direcciones
$ ruby scriptserver
=gt Rails application started on http00003000
=gt Ctrl-C to shutdown server call with --help for options
[2005-12-06 232744] INFO WEBrick 131[2005-12-06 232744] INFO ruby 182 (2004-12-25) [i386-
mswin32][2005-12-06 232745] INFO WEBrickHTTPServerstart pid=3972
port=3000
Note el puerto que el servidor nos ha dedicado 3000 Este es el puerto que vamos aaccesar utilizando nuestro navegador favorito La direccion es localhost o 127001 Asiacute queen nuestro navegador iremos a http1270013000 Si todo salioacute bien veremos unapantalla de felicitaciones con instrucciones para configurar Apache y pasos siguientes
Ahora que tenemos nuestra aplicacioacuten de rails lista podemos comenzar a ver como estaacutecompuesta nuestra nueva aplicacioacuten
Tu ambiente
Rails 11 tiene un enlace llamado About yourApplications environment que le permite verificarinformacioacuten acerca del ambiente Esta informacioacuten seencuentra en el URL rails_infoproperties
Anatomiacutea de una Aplicacioacuten Rails
Una aplicacioacuten rails se encuentra en el subdirectorio app y se compone de los siguientesdirectorios
apis - las libreriacuteas que su programa requiere fuera de Rails mismocontrollers - Los controladoreshelpers - Los helpersmodels - Los modelos Basicamente las clases que representan los datos que nuestra aplicacioacutenmanipularaacuteviews - Las vistas que son archivos rhtml (como JSP o ASP)
viewslayouts - Los disentildeos Cada controlador tiene su propio disentildeo donde se pondran las cabeceras ypies de paacuteginaconfig - Archivos de configuracioacuten La configuracioacuten de la base de datos se encuentra aquiacutescript - Utileriacuteas para generar coacutedigo y ejecutar el servidorpublic - La raiacutez del directorio virtual Cualquier contenido que usted encuentre aquiacute seraacute publicado en suaplicacioacuten directamente En una nueva aplicacioacuten usted puede encontrar las paacuteginas web para loserrores 404 y 500 las imaacutegenes javascripts estilos etceacuteteratest - Los archivos para las pruebas funcionales y de unidad
Llamando coacutedigo
Seguramente querremos que nuestro directorio tenga una pantalla principalConceptualmente llamaremos a esto el moacutedulo principal Asiacute que lo generamos
$ ruby scriptgenerate controller principal
exists appcontrollers
exists apphelpers
create appviewsprincipal
exists testfunctional
create appcontrollersprincipal_controllerrb
create testfunctionalprincipal_controller_testrb
create apphelpersprincipal_helperrb
Ahora puede usted visitar http1270013000principal para ver
Unknown actionNo action responded to index
El archivo que fue creado se llama controllersprincipal_controllerrb Vamos aponer nuestro coacutedigo ahiacute
class PrincipalController lt ApplicationController
def index
mensaje = Hola a todosltbrgt
[Pedro Pablo Juan]each do |nombre|
mensaje += y a +nombre+ tambienltbrgt
end
render text =gt mensaje
end
end
Cuando visitamos de nuevo vemos nuestro resultado
Hola a todosy a Pedro tambieny a Pablo tambieny a Juan tambien
Finalmente queremos que la raiacutez de esta aplicacioacuten sea tambieacuten nuestro controlador Asiacuteque eliminamos el indexhtml de bienvenida
$ rm publicindexhtml
Si vemos la paacutegina de nuevo ahora diraacute
Routing ErrorRecognition failed for
Esto es porque todaviacutea no le hemos dicho a la aplicacioacuten que nuestro controlador pordefecto es el principal Podemos hacer esto definiendo una ruta Asiacute que modificamos laconfiguracioacuten de rutas en el archivo configroutesrb
mapconnect controller =gt principal
Ahora cuando visitamos la paacutegina vemos el resultado de nuestro controlador principal
Si tiene dudas puede ver la peliacutecula
4 Teoriacutea El Paradigma MVC
MVC son las siglas de Modelo Vista y Controlador Es el patroacuten de disentildeo de software muacuteycomuacuten en programas interactivos orientados a objetos
Bajo el patroacuten de disentildeo de MVC dentro de un programa cada dominio loacutegico de edicioacuten(por ejemplo datos personales cuentas bancarias artiacuteculos noticiosos etceacutetera) necesitatres partes
1 El modelo que es una representacioacuten de objetos del dominio loacutegico En el ejemplo de datos personalesobjetos como Persona Direccioacuten y Teleacutefono
2 La vista que es una o varias piezas de coacutedigo que formatean y muestran todo o parte del modelo en eldispositivo interactivo (tiacutepicamente la pantalla) La vista tiacutepicamente es notificada de cambios en elmodelo
3 El controlador que interpreta la informacioacuten que el usuario provee para hacer cambios en el modelo
La idea es que al aplicar esta separacioacuten se hace posible crear maacutes de una vista para elmismo modelo (digamos una vista abreviada y una vista detallada) y reutilizar el modelo (yel coacutedigo que guarda el modelo de manera permanente) para escribir utileriacuteas relacionadaso incorporar datos del dominio original en programas maacutes grandes
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Ahora que tenemos nuestra aplicacioacuten de rails lista podemos comenzar a ver como estaacutecompuesta nuestra nueva aplicacioacuten
Tu ambiente
Rails 11 tiene un enlace llamado About yourApplications environment que le permite verificarinformacioacuten acerca del ambiente Esta informacioacuten seencuentra en el URL rails_infoproperties
Anatomiacutea de una Aplicacioacuten Rails
Una aplicacioacuten rails se encuentra en el subdirectorio app y se compone de los siguientesdirectorios
apis - las libreriacuteas que su programa requiere fuera de Rails mismocontrollers - Los controladoreshelpers - Los helpersmodels - Los modelos Basicamente las clases que representan los datos que nuestra aplicacioacutenmanipularaacuteviews - Las vistas que son archivos rhtml (como JSP o ASP)
viewslayouts - Los disentildeos Cada controlador tiene su propio disentildeo donde se pondran las cabeceras ypies de paacuteginaconfig - Archivos de configuracioacuten La configuracioacuten de la base de datos se encuentra aquiacutescript - Utileriacuteas para generar coacutedigo y ejecutar el servidorpublic - La raiacutez del directorio virtual Cualquier contenido que usted encuentre aquiacute seraacute publicado en suaplicacioacuten directamente En una nueva aplicacioacuten usted puede encontrar las paacuteginas web para loserrores 404 y 500 las imaacutegenes javascripts estilos etceacuteteratest - Los archivos para las pruebas funcionales y de unidad
Llamando coacutedigo
Seguramente querremos que nuestro directorio tenga una pantalla principalConceptualmente llamaremos a esto el moacutedulo principal Asiacute que lo generamos
$ ruby scriptgenerate controller principal
exists appcontrollers
exists apphelpers
create appviewsprincipal
exists testfunctional
create appcontrollersprincipal_controllerrb
create testfunctionalprincipal_controller_testrb
create apphelpersprincipal_helperrb
Ahora puede usted visitar http1270013000principal para ver
Unknown actionNo action responded to index
El archivo que fue creado se llama controllersprincipal_controllerrb Vamos aponer nuestro coacutedigo ahiacute
class PrincipalController lt ApplicationController
def index
mensaje = Hola a todosltbrgt
[Pedro Pablo Juan]each do |nombre|
mensaje += y a +nombre+ tambienltbrgt
end
render text =gt mensaje
end
end
Cuando visitamos de nuevo vemos nuestro resultado
Hola a todosy a Pedro tambieny a Pablo tambieny a Juan tambien
Finalmente queremos que la raiacutez de esta aplicacioacuten sea tambieacuten nuestro controlador Asiacuteque eliminamos el indexhtml de bienvenida
$ rm publicindexhtml
Si vemos la paacutegina de nuevo ahora diraacute
Routing ErrorRecognition failed for
Esto es porque todaviacutea no le hemos dicho a la aplicacioacuten que nuestro controlador pordefecto es el principal Podemos hacer esto definiendo una ruta Asiacute que modificamos laconfiguracioacuten de rutas en el archivo configroutesrb
mapconnect controller =gt principal
Ahora cuando visitamos la paacutegina vemos el resultado de nuestro controlador principal
Si tiene dudas puede ver la peliacutecula
4 Teoriacutea El Paradigma MVC
MVC son las siglas de Modelo Vista y Controlador Es el patroacuten de disentildeo de software muacuteycomuacuten en programas interactivos orientados a objetos
Bajo el patroacuten de disentildeo de MVC dentro de un programa cada dominio loacutegico de edicioacuten(por ejemplo datos personales cuentas bancarias artiacuteculos noticiosos etceacutetera) necesitatres partes
1 El modelo que es una representacioacuten de objetos del dominio loacutegico En el ejemplo de datos personalesobjetos como Persona Direccioacuten y Teleacutefono
2 La vista que es una o varias piezas de coacutedigo que formatean y muestran todo o parte del modelo en eldispositivo interactivo (tiacutepicamente la pantalla) La vista tiacutepicamente es notificada de cambios en elmodelo
3 El controlador que interpreta la informacioacuten que el usuario provee para hacer cambios en el modelo
La idea es que al aplicar esta separacioacuten se hace posible crear maacutes de una vista para elmismo modelo (digamos una vista abreviada y una vista detallada) y reutilizar el modelo (yel coacutedigo que guarda el modelo de manera permanente) para escribir utileriacuteas relacionadaso incorporar datos del dominio original en programas maacutes grandes
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
viewslayouts - Los disentildeos Cada controlador tiene su propio disentildeo donde se pondran las cabeceras ypies de paacuteginaconfig - Archivos de configuracioacuten La configuracioacuten de la base de datos se encuentra aquiacutescript - Utileriacuteas para generar coacutedigo y ejecutar el servidorpublic - La raiacutez del directorio virtual Cualquier contenido que usted encuentre aquiacute seraacute publicado en suaplicacioacuten directamente En una nueva aplicacioacuten usted puede encontrar las paacuteginas web para loserrores 404 y 500 las imaacutegenes javascripts estilos etceacuteteratest - Los archivos para las pruebas funcionales y de unidad
Llamando coacutedigo
Seguramente querremos que nuestro directorio tenga una pantalla principalConceptualmente llamaremos a esto el moacutedulo principal Asiacute que lo generamos
$ ruby scriptgenerate controller principal
exists appcontrollers
exists apphelpers
create appviewsprincipal
exists testfunctional
create appcontrollersprincipal_controllerrb
create testfunctionalprincipal_controller_testrb
create apphelpersprincipal_helperrb
Ahora puede usted visitar http1270013000principal para ver
Unknown actionNo action responded to index
El archivo que fue creado se llama controllersprincipal_controllerrb Vamos aponer nuestro coacutedigo ahiacute
class PrincipalController lt ApplicationController
def index
mensaje = Hola a todosltbrgt
[Pedro Pablo Juan]each do |nombre|
mensaje += y a +nombre+ tambienltbrgt
end
render text =gt mensaje
end
end
Cuando visitamos de nuevo vemos nuestro resultado
Hola a todosy a Pedro tambieny a Pablo tambieny a Juan tambien
Finalmente queremos que la raiacutez de esta aplicacioacuten sea tambieacuten nuestro controlador Asiacuteque eliminamos el indexhtml de bienvenida
$ rm publicindexhtml
Si vemos la paacutegina de nuevo ahora diraacute
Routing ErrorRecognition failed for
Esto es porque todaviacutea no le hemos dicho a la aplicacioacuten que nuestro controlador pordefecto es el principal Podemos hacer esto definiendo una ruta Asiacute que modificamos laconfiguracioacuten de rutas en el archivo configroutesrb
mapconnect controller =gt principal
Ahora cuando visitamos la paacutegina vemos el resultado de nuestro controlador principal
Si tiene dudas puede ver la peliacutecula
4 Teoriacutea El Paradigma MVC
MVC son las siglas de Modelo Vista y Controlador Es el patroacuten de disentildeo de software muacuteycomuacuten en programas interactivos orientados a objetos
Bajo el patroacuten de disentildeo de MVC dentro de un programa cada dominio loacutegico de edicioacuten(por ejemplo datos personales cuentas bancarias artiacuteculos noticiosos etceacutetera) necesitatres partes
1 El modelo que es una representacioacuten de objetos del dominio loacutegico En el ejemplo de datos personalesobjetos como Persona Direccioacuten y Teleacutefono
2 La vista que es una o varias piezas de coacutedigo que formatean y muestran todo o parte del modelo en eldispositivo interactivo (tiacutepicamente la pantalla) La vista tiacutepicamente es notificada de cambios en elmodelo
3 El controlador que interpreta la informacioacuten que el usuario provee para hacer cambios en el modelo
La idea es que al aplicar esta separacioacuten se hace posible crear maacutes de una vista para elmismo modelo (digamos una vista abreviada y una vista detallada) y reutilizar el modelo (yel coacutedigo que guarda el modelo de manera permanente) para escribir utileriacuteas relacionadaso incorporar datos del dominio original en programas maacutes grandes
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Esto es porque todaviacutea no le hemos dicho a la aplicacioacuten que nuestro controlador pordefecto es el principal Podemos hacer esto definiendo una ruta Asiacute que modificamos laconfiguracioacuten de rutas en el archivo configroutesrb
mapconnect controller =gt principal
Ahora cuando visitamos la paacutegina vemos el resultado de nuestro controlador principal
Si tiene dudas puede ver la peliacutecula
4 Teoriacutea El Paradigma MVC
MVC son las siglas de Modelo Vista y Controlador Es el patroacuten de disentildeo de software muacuteycomuacuten en programas interactivos orientados a objetos
Bajo el patroacuten de disentildeo de MVC dentro de un programa cada dominio loacutegico de edicioacuten(por ejemplo datos personales cuentas bancarias artiacuteculos noticiosos etceacutetera) necesitatres partes
1 El modelo que es una representacioacuten de objetos del dominio loacutegico En el ejemplo de datos personalesobjetos como Persona Direccioacuten y Teleacutefono
2 La vista que es una o varias piezas de coacutedigo que formatean y muestran todo o parte del modelo en eldispositivo interactivo (tiacutepicamente la pantalla) La vista tiacutepicamente es notificada de cambios en elmodelo
3 El controlador que interpreta la informacioacuten que el usuario provee para hacer cambios en el modelo
La idea es que al aplicar esta separacioacuten se hace posible crear maacutes de una vista para elmismo modelo (digamos una vista abreviada y una vista detallada) y reutilizar el modelo (yel coacutedigo que guarda el modelo de manera permanente) para escribir utileriacuteas relacionadaso incorporar datos del dominio original en programas maacutes grandes
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
4 Teoriacutea El Paradigma MVC
MVC son las siglas de Modelo Vista y Controlador Es el patroacuten de disentildeo de software muacuteycomuacuten en programas interactivos orientados a objetos
Bajo el patroacuten de disentildeo de MVC dentro de un programa cada dominio loacutegico de edicioacuten(por ejemplo datos personales cuentas bancarias artiacuteculos noticiosos etceacutetera) necesitatres partes
1 El modelo que es una representacioacuten de objetos del dominio loacutegico En el ejemplo de datos personalesobjetos como Persona Direccioacuten y Teleacutefono
2 La vista que es una o varias piezas de coacutedigo que formatean y muestran todo o parte del modelo en eldispositivo interactivo (tiacutepicamente la pantalla) La vista tiacutepicamente es notificada de cambios en elmodelo
3 El controlador que interpreta la informacioacuten que el usuario provee para hacer cambios en el modelo
La idea es que al aplicar esta separacioacuten se hace posible crear maacutes de una vista para elmismo modelo (digamos una vista abreviada y una vista detallada) y reutilizar el modelo (yel coacutedigo que guarda el modelo de manera permanente) para escribir utileriacuteas relacionadaso incorporar datos del dominio original en programas maacutes grandes
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
5 Teoriacutea Pruebas de Unidad
Introduccioacuten
En cualquir sistema complejo es criacutetico probarlo praa saber que funcionaraacute una vez que selance en vivo Muchos grupos de trabajo se limitan a dedicar una o dos personas a probar elsistema Eacuteste meacutetodo tiene algunos problemas
1 El programador estaacute separado de la prueba Esto impide que el programador proporcione informacioacutenacerca de condiciones extremas de prueba y el aislamiento tambieacuten provoca que el programador sigacometiendo los mismos errores por no tener informacioacuten acerca de como se prueba su sistema
2 Una prueba que se limita a utilizar el sistema como un usuario no cubre problemas de disentildeo de objetosMientras el programa crece el coacutedigo espagueti se hace maacutes y maacutes complejo El problema se hace peorexponencialmente mientras el sistema se mantenga en esta situacioacuten hasta que arreglarlo se vuelve maacutescaro que comenzar de nuevo
3 Como humanos nos es faacutecil olvidar maneras alternativas de utilizar las mismas funciones Por ejemplo iquestutilizas ArchivoGuardar el boton para guardar o Control-S iquestSi es ArchivoGuardar conel teclado o con el mouse iquestCogravemo sabes que los dos funcionan Todas estas condiciones parecenmultiplicarse diario
Lo que las pruebas de unidad buscan es evitar dichos problemas para que la calidad delsisterma sea mayor Es un hecho comprobado que los sistemas que estaacuten disentildeadosutilizando metodologiacuteas que ponen la prueba como parte integral del disentildeo terminan siendomaacutes confiables
iquestQueacute es una Prueba de Unidad
Una prueba de unidad pretende probar cada funciograven en un archivo de programa simple (unaclase en terminologigravea de objetos) Las libreriacuteas de pruebas de unidad formalizan este trabajoal proporcionar clases para pruebas
La prueba de unidad ayuda a que el mogravedulo se haga independiente Esto quiere decir que unmogravedulo que tiene una prueba de unidad se puede probar independientemente del resto delsistema Una vez que un gran porcentaje de su programa cuente con pruebas de unidad
Note que las pruebas de unidad no sustituyen a las pruebasfuncionales del departamento de control de calidad pero a lalarga reducen la cantidad de errores de regresioacuten en futurasversiones del producto
Una buena prueba de unidad
No requiere de mucho cogravedigo para prepararla- Una prueba de unidad cuya preparaciograven toma 3 minutos(conectagravendose a la base de datos levantando un servidor de web etcegravetera) se debe considerardemasiado pesada para ejecutarse cada vez que se hagan cambiosPrueba soacutelo un meacutetodo Aunque no siempre es necesario hacer una prueba por meacutetodo siacute esimportante asegurar que cubrimos toda la clase Muchas veces se requiere maacutes de un meacutetodo de pruebapor cada meacutetodo en la clase para poder probar diferentes datos y casos extremos (por ejemplo iquestqueacutepasa cuendo el usuario no existe)Verifica los resultados con aserciones De preferencia se debe de checar soacutelo un resultado Se puedenchecar varias partes del mismo resultado pero diferentes condiciones deben probarse en un meacutetodoseparado
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Deja los datos de la misma manera en que los encontroacute Cuando la prueba comienza los datos sepreparan si es necesario pero cuando la prueba termina es importante que el sistema borre los datosdespueacutes de la verificacioacuten Es por esto que muchas libreriacuteas de pruebas unitarias tienen meacutetodosllamados setup() y teardown() o similares
Pruebas de Unidad y Disentildeo de Software
Las pruebas de unidad influenciacutean el disentildeo (para bien) Estoquiere decir que el aplicar pruebas de unidad a un sistemapreviamente escrito seraacute difiacutecil Cuando usted se decida a antildeadirpruebas de unidad a un sistema existente aseguacuterese de escojersus batallas y hacer pruebas de regresioacuten del sistema Si susistema es mediano o grande es conveniente planear sus pruebasy aplicar los cambios necesarios a traves de varias versionesfuturas No lo intente todo al mismo tiempo
Cobertura
Otro concepto relacionado a las pruebas de unidad es el de cobertura iquestCoacutemo sabemoscuantas liacuteneas del programa estaacuten siendo probadas (y maacutes importante las que no seprobaron) Una vez que usted tiene pruebas de unidad probablemente le interesaraacute tenerun reporte de las liacuteneas que no se han probado Una utileriacutea gratuita para proporcionar estosreportes en ruby se llama apropriadamente rubycoverage Rubycoverage no viene con rails- usted lo debe instalar utilizando gems
Desarrollo Basado en Pruebas (o Metodologiacutea PruebaPrimero)
Mencionamos anteriormente que las pruebas de unidad influenciacutean positivamente el disentildeode su programa Es por esto que muchas metodologiacuteas aacutegiles son partidarias de lo que sellama probar primero
La idea del desarrollo basado en pruebas consiste en utilizar las pruebas de unidad comouna pre-verificacioacuten y guiacutea para comenzar a escribir el coacutedigo Al escribir la prueba primero(o al menos al mismo tiempo que el moacutedulo original) a la larga es maacutes sencillo mantener elprograma y el disentildeo nunca se perjudica al punto que crea dificultades en el control decalidad
La importancia de Pruebas de Unidad en Rails
En ruby on rails y en todos los lenguajes interpretados en uso hoydiacutea la idea de las pruebas de unidad es central e importantiacutesimaComo casi todos los errores en un programa interpretado soacuteloocurren cuando el programa corre (porque no hay etapa decompilacioacuten) en esta clase de lenguajes es esencial escribir laspruebas de unidad al mismo tiempo que el coacutedigo y asegurar unacobertura adecuada
Las pruebas de unidad y el desarrollo basado en pruebas sonimportantes en todos los lenguajes pero son auacuten maacutes importantesen Ruby y Rails No lo olvide
Por este motivo durante el curso veremos coacutemo podemos escribirnuestras pruebas primero (o al menos al mismo tiempo) que el
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
resto del sistema
Basado en el requerimiento primero escribimos una prueba inicial que nos ayudariacutea adeterminar que el requerimiento funciona correctamente en su camino feliz o sea su caminomaacutes sencillo Obviamente la prueba debe fallar porque todaviacutea no hay coacutedigo de verdad
Ahora que la prueba falla podemos comenzar a escribir la implementacioacuten para que laprueba funcione de la manera maacutes sencilla como sea posible
Una vez que tengas tu implementacioacuten inicial podemos hechar un vistazo a los casosexcepcionales Por ejemplo si escribimos un requerimiento que dice que los usuariospueden subscribirse a nuestro sistema ellos mismos y que el sistema les enviaraacute un correoelectroacutenico cuando se suscriban iquestqueacute pasa si el usuario es menor de edad iquestQueacute pasa siel usuario escribe un correo electroacutenico no vaacutelido La idea es crear suficientes pruebas paralos casos diferentes tiacutepicos
Pruebas de Unidad y Rails
Ruby on Rails utiliza la libreriacutea TestUnit como estaacutendar para crear pruebas de unidadCuando usted utiliza los generadores de Rails para crear cualquier moacutedulo ya sea modeloso controladores rails automaacuteticamente crea una prueba de unidad para dicho controlador omodelo en el directorio test incluyendo una que otra prueba baacutesica El disentildeo MVC deRails hace maacutes faacutecil escribir pruebas de unidad Cuando usted extienda el programa soacutelotiene que mantener las pruebas
Ademaacutes el comando rake en una aplicacioacuten de rails ejecuta las pruebas De esta manerausted puede antildeadir las pruebas de unidad y ejecutarlas con un soacutelo comando
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
6 Configurando la Base de Datos
Creando una base de Datos en MySQL
Ahora vamos a preparar sus bases de datos Rails utiliza configuraciones por separado paradesarrollo (development) prueba (test) y produccioacuten La idea es que usted desarrolla yprueba en una base de datos y ejecuta las pruebas de unidad apuntando a la base de datosde prueba En general rails no toca el esquema de la base de datos de produccioacuten
La recomendacioacuten es que utilice usted los sufijos _dev para desarrollo _test para pruebay _prod para produccioacuten y que su nombre baacutesico de la base de datos sea el mismo
Notas Acerca de Rails y bases de datos
Por defecto Rails utiliza bases de datos sqlite para desarrolloEsto no tiene nada de malo pero sqlite estaacute disentildeado paraaplicaciones pequentildeas y muy raacutepidas y para implementacioneslocales (sqlite3 es el motor de almacenamiento local para elsubsistema Core Data de Mac OS X por ejemplo) Paraimplementacioacuten de un sistema web donde varios servidores deweb estaacuten conectados a la misma base de datos es preferibleutilizar una base de datos como MySQL o Postgres para queevitar un problema de escalabilidad
Preparando la base de datos de Desarrollo
Para preparar su base de datos de desarrollo en MySQL ejecute los siguientes comandos
$ mysql -h miservidormydominiocom -u root -ppassword mysqlgt create database direcciones_devQuery OK 1 row affected (053 sec)mysqlgt grant all privileges on direcciones_dev to direcciones_dev -gt identified by direccionesQuery OK 0 rows affected (080 sec)mysqlgt Control-DBye
Configurando su Aplicacioacuten
Para configurar su base de datos usted necesita editar el archivo configdatabaseyml para apuntar a su servidor
development adapter mysql database direcciones_dev host miservidormydominiocom username direcciones_dev password direcciones test adapter mysql
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
database direcciones_test host miservidormydominiocom username direcciones_test password direcciones production development
Ahora su sistema se puede conectar a la base de datos en la base de datos de desarrolloHaga lo mismo con la base de datos de prueba (crear la base de datos su usuario y losprivilegios en MySQL)
Una vez que su programa esteacute listo su administrador de sistema puede configurar la basede datos del sistema de produccioacuten dentro de este mismo archivo Un sistema se puedeponer en modo de produccioacuten cambiando el valor RAILS_ENV en el archivoconfigenvironmentrb
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
7 Creando un Moacutedulo con Modelo Controlador y Vista
Planeando nuestro modelo
Hemos mencionado que lo que queremos hacer en este curso es escribir una libreta dedirecciones Asiacute que comenzaremos a planear nuestro modelo
Diferentes Maneras de crear un Modelo
Hay tres maneras de crear un modelo en un sistema
Pantallas No es muy recomendable pero puede uno basarseen un boceto de las pantallasModelo de Objetos Si usted estaacute acostumbrado a utilizarJava yo a hacer programas que no utilicen bases de datospuede ser que usted utilice un modelo de classes y objetosModelo de base de datos Si usted crea muchasaplicaciones de bases de datos (o es administrador de bases dedatos) usted probablemente crea su modelo de base de datosprimero
El modelo de rails es una combinacioacuten de modelo de base de datosy modelo de objetos En el paradigma de disentildeo tiacutepico ustedmodela las tablas y relaciones de bases de datos y despueacutes crea elcoacutedigo del modelo Eacutesta es la manera recomendable de disentildear elprograma
Creando la tabla en la base de datos
La mejor manera de crear la tabla es utilizando una migracioacuten Una migracioacuten es unconcepto de ActiveRecord que le permite mantener las modificaciones al esquema de subase de datos sincronizadas con la version actual de su coacutedigo Para crear una migracioacutenusted debe hacer lo siguiente
$ cd direcciones$ ruby scriptgenerate migration crea_tabla_personas create dbmigrate create dbmigrate20081102001338_crea_tabla_personasrb
Rails ahora ha creado una clase llamada CreaTablaPersonas (en el archivodbmigrate20081102001338_crea_tabla_personasrb que contiene los meacutetodos selfup yselfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup end
def selfdown endend
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Una migracioacuten define los meacutetodos selfup y selfdown para hacer y deshacerrespectivamente sus cambios al esquema Para crear nuestra tabla definimos la tabla conun comando de ruby en selfup Tambieacuten escribiremos el coacutedigo para deshacer nuestroscambios (en este caso eliminar la tabla) en selfdown
class CreaTablaPersonas lt ActiveRecordMigration def selfup create_table personas do |tabla| tablastring nombre limit =gt 80 null =gt false tablastring apellido_paterno limit =gt 80 tablastring apellido_materno limit =gt 80 El nombre que debemos desplegar en pantalla tablastring desplegar limit =gt 80 tablastring sexo limit =gt 10 default =gt Hombre null =gt false tablatext notas end end
def selfdown drop_table personas endend
El resultado de ejecutar la migracioacuten seraacute el siguiente SQL (en mysql)
CREATE TABLE personas ( id int NOT NULL auto_increment nombre varchar(80) default NOT NULL apellido_paterno varchar(80) default NULL apellido_materno varchar(80) default NULL desplegar varchar(80) default NULL sexo varchar(10) default Hombre notas TEXT NULL PRIMARY KEY (id))
Note que el campo identificador (id) se crea automaacuteticamente
Cuando su coacutedigo esteacute listo simplemente ejecute el comando
$ rake dbmigrate
Y el sistema se conectaraacute a la base de datos y ejecutaraacute la migracioacuten
Rails mantiene una tabla en la base de datos llamada schema_info que contiene la versioacutenactual de la base de datos Si usted ejecuta el comando rake migrate VERSION=x la base dedatos cambiaraacute a la versioacuten X ya sea ejecutando los meacutetodos up de las migraciones queno se han hecho o ejecutando los meacutetodos down de las versiones que hay que revertir
Migraciones Irreversibles
De vez en cuando usted necesitaraacute hacer unamigracioacuten irreversible (por ejemplo si necesitaeliminar una columna de la base de datos) Cuandoesto ocurra iexclno deje selfdown vaciacuteo Utiliceraise IrreversibleMigration para que el sistema sepaque los cambios en selfup son irreversibles
Creando un Andamio
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Ahora que tenemos nuestro modelo en la base de datos podemos decirle a rails que creenuestro modelo controlador y vista con un soacutelo comando ejecutando el programa generatecon el nombre de nuestra tabla en MySQL
$ ruby scriptgenerate scaffold persona
exists appmodels
exists testunit
exists testfixtures
create appmodelspersonarb
create testunitpersona_testrb
create testfixturespersonasyml
Para ver como se creoacute el modelo veamos el coacutedigo de personarb
class Personas lt ActiveRecordBase
end
Es algo que puede escribir usted mismo pero usar los generadores le ahorra tiempo y evitaerrores
iquestY las propiedades
Notaraacute que su clase Personas no tiene propiedadesequivalentes a los campos de la base de datos Estoes porque Ruby automaacuteticamete crea propiedadespara los campos de la tabla de Personas en la basede datos Recuerde Ruby utiliza el principio No teRepitas
Esto es muy uacutetil especialmente porque cuandoestamos comenzando en nuestro proyecto muchasveces tenemos que cambiar los nombres de loscampos hasta que los prototipos y el coacutedigo seestabilice
El generador ha creado lo que llamamos un andamiaje (scaffolding)
iquestAndamio iexclSi no soy albantildeil
En construccioacuten un andamio (scaffold) es unaconstruccioacuten muy sencilla que hace posible laconstruccioacuten del edificio real De la misma maneraun el generador crea una construccioacuten sencilla enRails que nos permite antildeadir registros a la base dedatos de inmediato y nos sirve de andamiajehaciendo posible construir el programa real
Ahora podemos comenzar nuestro servidor y navegar a http1270013000personas paraver nuestro andamio y crear algunos registros de prueba
Como veraacute su andamio contiene un listado vista actualizacioacuten y borrado de registros Estoes lo que le llaman en ingleacutes CRUD por las siglas de createreplaceupdatedelete Sulistado tambieacuten le muestra paacuteginas Trate de antildeadir maacutes de diez registros para verlofuncionar
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
SingularPlural Note que el estaacutendar de Rails es utilizarplural para los nombres de las tablas en la base de datos (createtable personas) y singular para ejecutar los generadores Elgenerador pluralizaraacute el nombre cuando lo considere adecuado
Rails pluralizoacute automaacuteticamente el nombre persona que le dimos al generador Laestructura pluralsingular queda como sigue
Tabla (en Base de Datos) PERSONAS PluralModelo (en appmodels) personarb SingularVista (en appviews) personasrhtml PluralControlador (en appcontrollers) pesonas_controllerrb PluralPrueba (en testsfunctional) personas_controller_testrb Plural
La idea de la regla es que la uacutenica mencioacuten de singular es cuando creamos el modelo
Si esto no es adecuado a su proyecto usted puede cambiar algunas de estas reglascambiando el archivo configenvironmentrb Algunos de los cambios que le interestaraacutenson
ActiveRecordBasepluralize_table_names (truefalse) le permite decirle a rails que pluralize ono los nombres de las tablas Si su administrador de base de datos no quiere que las tablas esteacuten enplural escriba ActiveRecordBasepluralize_table_names = falseActiveRecordBaseprimary_key_prefix_type (table_nametable_name_with_underscore) lepermite definir la manera en que ActiveRecord calcularaacute el campo identificador por defecto En nuestroejemplo con el valor table_name el campo identificador seriacutea personaid pero con la opcioacutentable_name_with_underscore el campo identificador seriacutea persona_id
El Inflector
ActiveRecord utiliza una clase llamada Inflector para especificar las reglas de pluralizacioacutenUsted puede modificar y antildeadir nuevas reglas de pluralizacioacuten para su aplicacioacuten tambieacutencon configenvironmentrb Simplemente agregue otras reglas utilizando expresionesregulares para definir inflecciones del plural singular irregular o imposible de contar (notiene plural) como sigue
Inflectorinflections do |inflect| inflectplural ^(ox)$i 1en inflectsingular ^(ox)eni 1 inflectirregular person people inflectuncountable w( fish sheep )end
Pero mi Administrador de Base de Datos es muy Estricto
Si su administrador de DB es muy estricto (o la base de datos pertenece a otro programa yno se puede cambiar) usted puede cambiar el nombre de la base de datos en el modelopersonarb cambiando el meacutetodo table_name Por ejemplo trabajamos en un banco enormey tienen estaacutendares de esos que obligan a utilizar nombres ilegibles (pobrecitos) de soacuteloocho caracteres para que sean compatibles con su computadora mainframe de 1970(ouch) y el nombre de la tabla de persona debe ser DC_PERSN
class Personas lt ActiveRecordBase
def selftable_name() DC_PERSN end
end
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Listo
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
8 Control de Versiones con Subversion
Subversion es el sistema de control de versiones que utilizaremos a traveacutes de este curso Nodepende ni tiene nada que ver con Ruby o Rails de hecho usted puede utilizar subversioncon cualquier lenguaje de programacioacuten (incluso con cualquier coleccioacuten de archivos paralos cuales usted desee mantener un historial de versiones)
Nota Esto es soacutelo una introduccioacuten baacutesica a subversion paraque pueda comprender los comandos de subversion queejecutareacute de vez en cuando en el curso de rails El tema decontrol de versiones en siacute mismo es un tema enorme Para unamucha mejor explicacioacuten en espantildeol de coacutemo funcionasubversion el libro en liacutenea Control de Versiones conSubversion es mucho maacutes adecuado (y tambieacuten es gratuito)
Programacioacuten y Versiones
Si usted ha escrito programas con anterioridad seguramente le resultaraacute familiar la historiade Juan Loacutepez programador
Ayer como a las 1000pm mi programa funcionabaperfectamente Estaba yo haciendo cambios pequentildeos nada maacutespara limpiar el coacutedigo y me quedeacute hasta las 2 de la mantildeanahaciendo mejoras esteacuteticas pequentildeitas
Ahora son las 700am y tengo una demostracioacuten con el cliente enuna hora iexcliexcliexcly el programa no funciona No se que le hice peroojalaacute hubiera hecho una copia a las 1000pm
Este es uno de los muchos motivos por los que todo programador necesita un sistema decontrol de versiones Con un sistema de control de versiones nuestro hipoteacutetico Juan Loacutepezpodriacutea haber vuelto a la versioacuten que funcionaba perfectamente a las 8 de la noche y nohubiera perdido el cliente
La otra ventaja de un sistema de control de versiones es la posibilidad de comparar Con unsistema de control de versiones Juan hubiera podido hacer una comparacioacuten de losarchivos de su versioacuten con la versioacuten de las ocho de la noche para ver uacutenicamente lasdiferencias Como soacutelo necesitariacutea examinar las diferencias podriacutea haber encontrado elerror raacutepidamente y no tendriacutea que perder muchos de sus cambios esteacuteticos para recuperarel funcionamiento de su programa
Subversion
En este curso utilizaremos el sistema de control de versiones llamado subversionSubversion es un sistema disentildeado para reemplazar otro sistema de control de versionesllamado CVS CVS es el sistema de control de versiones usado en gran parte del mundo dela programacioacuten de fuente abierta Varios de los comandos que utilizaremos aquiacute tambieacutenson vaacutelidos en CVS
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Instalacioacuten en Unix
Si usted estaacute en un sistema Unix primero revise su versioacuten de subversion - algunasdistribuciones de Unix ya lo tienen Trate de ejecutar svn --version para ver si el programaresponde SI no lo tiene puede descargar los coacutedigos fuentes o los programas binarios de lapaacutegina de web de subversion Una vez instalado y configurado puede usted continuar paracrear su repositorio
Instalacioacuten en Windows
Para los usuarios de Windows hay dos opciones Usted puede utilizar los binarios de liacuteneade comando o una extensioacuten para Windows Explorer llamada TortoiseSVN que leproporcionaraacute menuacutes de contexto Recuerde que todas las demaacutes plataformas utilizan liacuteneade comando Este capiacutetulo explica coacutemo utilizar la liacutenea de comando En tortoiseSVN estonormalmente quiere decir hacer click con el botoacuten derecho en Windows y seleccionar elcomando con el mismo nombre en el menuacute de contexto
Creando un Repositorio
Un repositorio es un aacuterea en su sistema donde el sistema de control de versiones va aguardar su programa Su programa se guarda en una serie de archivos que guardan toda lahistoria de su programa Usted puede accesar toda la historia de las versiones de suprograma
Para crear un repositorio en subversion usted puede utilizar el comando svnadmin create
$ svnadmin create datosrepositoriosdirecciones
Esto crearaacute un repositorio llamado direcciones en el directorio datosrepositorio (que primerodebe existir) De preferencia (pero no obligatoriamente) usted debe hacer esto en unservidor para que se pueda conectar a el repositorio desde otras maacutequinas
Importando nuestro coacutedigo
Ahora que su repositorio ha sido creado podemos importar nuestro contenido Conanterioridad hemos creado una aplicacioacuten de rails que va a ser una libreta de direcciones ycreamos nuestro modelo de personas junto con el andamiaje Todo funciona bien hastaahora asiacute que lo que queremos es que esta sea la primera revisioacuten de nuestro programaAsiacute que importamos nuestro coacutedigo fuente del programa de direcciones especificando dondevive nuestro archivo
$ cd direcciones$ svn import filedatosrepositoriosdirecciones
iquestPorqueacute fileSubversion utiliza URNs para especificar donde vivesu repositorio Cuando usted utiliza svn sin unservidor el prefijo es file para acceder al sistemade archivos local En un ambiente multiusuario estiacutepico utilizar urns con http o ssh
A continuacioacuten svn abriraacute un editor de textos (en Unix el editor de textos es tiacutepicamente vi oel editor especificado en la variable $EDITOR) que contiene lo siguiente
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
--This line and those below will be ignored--
A
La A junto al punto decimal quiere decir que estamos Antildeadiendo el directorio actual(representado por un punto) Es la manera en la que svn nos dice que va a ocurrir una vezque salgamos del editor de textos El motivo por el cual el editor de textos es mostrado espara que usted describa los cambios que le ocurrieron al repositorio (para el archivohistoacuterico)
Usted ahora puede escribir una descripcioacuten de lo que le estaacute haciendo al repositorio En estecaso estamos apenas comenzando asiacute que escribimos algo como lo siguiente
Importando el sistema de direcciones
--This line and those below will be ignored--
A
Ahora guarde el archivo y salga del editor A continuacioacuten veraacute que subversion antildeade todoslos archivos en su directorio al repositorio
Adding test
Adding testunit
Adding testunitpersona_testrb
Adding testtest_helperrb
Adding testfunctional
Adding testfunctionalpersonas_controller_testrb
Adding testfixtures
Adding testfixturespersonasyml
Adding testmocks
Adding testmockstest
Adding testmocksdevelopment
Adding app
Adding apphelpers
Adding apphelpersapplication_helperrb
Adding apphelperspersonas_helperrb
Adding appmodels
Adding appmodelspersonarb
Adding appcontrollers
Adding publicfaviconico
Committed revision 1
Nuestro repositorio ahora ha sido importado de este directorio Ahora necesitamossincronizar con el repositorio para que pueda mantener nuestros cambios Para hacer esonecesitamos primero jalar una copia que provenga del repositorio asiacute que cambiamos elnombre del directorio de nuestro programa (lo borraremos despueacutes que nos aseguremosque todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilicenuestro repositorio
$ cd
$ mv direcciones direccionesborrame
$ svn checkout filedatosrepositoriosdirecciones
A direccionestest
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
A direccionestestunit
A direccionestestunitpersona_testrb
A direccionestesttest_helperrb
A direccionestestfunctional
A direccionestestfunctionalpersonas_controller_testrb
A direccionestestfixtures
A direccionestestfixturespersonasyml
A direccionestestmocks
A direccionestestmockstest
A direccionestestmocksdevelopment
Checked out revision 1
$ cd direcciones
Modificando Archivos
Ahora tenemos una copia que proviene del repositorio Esta copia seraacute actualizada cuandonosotros ejecutemos el comando svn commit desde el directorio de direcciones Podemosactualizar subdirectorios independientemente depende de donde ejecutemos el comandocommit Ademaacutes cambios a varios archivos en el repositorio se realizan atoacutemicamente (setoman en cuenta como uno) Por ejemplo haga algunos cambios a su archivodocREADME_FOR_APP con un editor de texto (este archivo es uacutetil para describir de que setrata su programa y como instalarlo) y despueacutes ejecute svn commit
$ svn commit
Ahora veraacute que su editor abre y muestra que ha habido un cambio al archivo
--This line and those below will be ignored--
M docREADME_FOR_APP
Escriba de que se tratoacute el cambio (por ejemplo Mejor explicacioacuten de nuestro programa) ysalga del editor Subversion guardaraacute el cambio
Sending docREADME_FOR_APP
Transmitting file data
Committed revision 2
Ahora el repositorio se encuentra en la revisioacuten 2
iquestQueacute significan A y MSubversion utiliza letras para explicarle queacute le ocurrea los archivos Las letras son como sigue
A Este archivo ha sido antildeadido (Added)M Este archivo ha sido modificado (Modified)D Este archivo ha sido elimidado (Deleted)
Historial de un archivo
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Para ver la historia de un archivo utilice el comando svn log
$ svn log docREADME_FOR_APP----------------------------------------------------------
--------------r2 | Owner | 2006-01-21 153914 -0800 (Sat 21 Jan 2006) | 2
lines
Mejor explicacion del programa
----------------------------------------------------------
--------------r1 | Owner | 2006-01-21 152801 -0800 (Sat 21 Jan 2006) | 3
lines
Importando el sistema de direcciones
----------------------------------------------------------
--------------
El sistema le explica los cambios que incluyeron este archivo
Antildeadiendo y eliminando archivos
Cuando usted antildeade y elimina archivos dentro de su sistema el sistema de control deversiones no efectuacutea los cambios hasta que usted especifique el comando svn add o svnrm (antildeadir y eliminar respectivamente) Asiacute usted no se necesita preocupar en eliminar unarchivo accidentalmente y de la misma manera no antildeadiraacute un archivo por error ensuciandola historia de su repositorio
Por ejemplo hagamos un archivo de prueba en el directorio doc Digamos que su nombrees pruebatxt
$ echo Hola como estas gt docpruebatxt
$ svn add docpruebatxt
A docpruebatxt
$ svn commit
Adding docpruebatxt
Transmitting file data
Committed revision 3
Ahora lo podemos eliminar
$ svn rm docpruebatxt
D docpruebatxt
$ svn commit
Deleting docpruebatxt
Committed revision 4
Actualizando el Repositorio
Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorioAl principio de cada sesioacuten de trabajo usted puede ejecutar el comando svn update parasincronizar de nuevo con el repositorio
$ svn update
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
U docREADME_FOR_APP
En este ejemplo alguien cambioacute README_FOR_APP y ahora ya tengo sus cambios
Comparando versiones
Usted puede comparar versiones utilizando el comando svn diff Si usted no especificasoacutelo el nombre del archivo el sistema compararaacute la copia del archivo en su disco duro conla copia maacutes nueva del repositorio
Por ejemplo algunos cambios a la versioacuten en disco duro de mi docREADME_FOR_APPAhora ejecutareacute svn diff
$ svn diff docREADME_FOR_APP
Index docREADME_FOR_APP===================================================================
--- docREADME_FOR_APP (revision 2)
+++ docREADME_FOR_APP (working copy)
-12 +13
-Programa de ejemplo para el curso de rails
+Esta es una nueva liacutenea
En este listado las liacuteneas que se eliminaron comienzan con el signo de menos y las que seantildeadieron con el signo de maacutes Las arrobas me dicen aproximadamente donde (liacutenea ycolumna) se encuentra el cambio
Finalmente no quiero quedarme con este cambio asiacute que mejor ejecuto svn revert para quemi copia se elimine y se reemplace con la del repositorio
$ svn revert docREADME_FOR_APPReverted docREADME_FOR_APP
Esto es soacutelo una introduccioacuten y subversion tiene muchos otros comandos Le recomiendoque revise la documentacioacuten y aprenda lo maacutes que pueda de este excelente sistema
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
9 Pruebas de Unidad
Preparando un Ambiente de Prueba
Como vimos cuando estabamos configurando la base de datos usted necesita tener unabase de datos separada para prueba Si usted ha seguido el tutorial y no creoacute una base dedatos de prueba la puede crear ahora (vaciacutea) simplemente ejecutando el comando CREATEDATABASE direcciones_test (o el nombre que tenga su base de datos)
Probando el Directorio
Ahora siacute estamos listos para crear las pruebas de nuestro directorio Rails ya antildeadioacute algunaspruebas a nuestro sistema esqueleto asiacute que intentaremos ejecutarlas
$ rake(in homedavidplaydirecciones)rubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testunitpersona_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 02 seconds
1 tests 1 assertions 0 failures 0 errorsrubybinruby -Ilibtest rubylibrubygems18gemsrake-062librakerake_test_loaderrb testfunctionalpersonas_controller_testrbLoaded suite rubylibrubygems18gemsrake-062librakerake_test_loaderStartedFinished in 0751 seconds
8 tests 28 assertions 0 failures 0 errors
iexclQueacute bueno pasamos iquestPero queacute se proboacute Veamos las pruebas que tenemos Losprogramas en el directorio test son
$ find test -name rbtestfunctionalpersonas_controller_testrbtesttest_helperrbtestunitpersona_testrb
Pruebas de Unidad vs Funcionales
En el esqueleto creado por rails las pruebas se dividen en pruebas de unidad (queidealmente prueban cada meacutetodo de nuestro programa) y pruebas funcionales que pruebanque su programa haga lo que usted espera
En general las pruebas funcionales prueban el controlador y las pruebas de unidad pruebanel modelo y las clases de soporte
Cuando usted creoacute el moacutedulo MVC de personas el generador de coacutedigo preparoacute esqueletos
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
para sus pruebas de unidad
Para ver de queacute se compone una prueba baacutesica veamos el archivo testunitpersona_testrb
require Filedirname(__FILE__) + test_helper
class PersonaTest lt TestUnitTestCase fixtures personas
Replace this with your real tests def test_truth assert_kind_of Persona personas(first) endend
Una prueba de unidad en ruby es una clase que hereda de TestUnitTestCase Dentro deesta clase nosotros escribimos las pruebas
Adornos (Fixtures)
A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datosEn terminologiacutea rails estos registros se llaman fixtures o adornos Se llaman asiacute porqueson uacutetiles primariamente para que la prueba tenga datos con los cuales trabajar
Los adornos para nuestra tabla se encuentran en el folder testfixturespersonasymlComo podraacute imaginar cuando creamos nuesto moacutedulo utilizando el generador rails nos hizoel favor de incluir este archivo de adorno para la tabla
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlfirst id 1another id 2
Lo que tenemos que hacer ahora es antildeadir nuestros datos iniciales El llenado de losadornos es muy sencillo y se hace de acuerdo a la descripcioacuten de la tabla que disentildeamoscon anterioridad
Read about fixtures at httparrubyonrailsorgclassesFixtureshtmlpersonaje_novela id 1 nombre Alonso apellido_paterno Quijano apellido_materno Cervantes desplegar Alonso Quijano sexo Hombre notas Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme
actor_famoso id 2 nombre Pedro apellido_paterno Infante apellido_materno Cruz desplegar Pedro Infante sexo Hombre notas El Idolo de Guamuchil
Note que en el formato YML podemos nombrar nuestros registros para describir su
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
naturaleza que nos ayudaraacute para que nuestras pruebas sean maacutes claras En este casocambiamos los nombres first y another por personaje_novela y actor_famoso Comocambiamos el nombre de nuestro primer registro nuestro meacutetodo test_truth en el listadoanterior tambieacuten necesita cambiar
Utilizando Adornos
Estas pruebas son un poco triviales pero hagamos un par de pruebas de unidad paracomprobar que esto funcione
def test_actor_famoso actorazo = personas(actor_famoso) assert_equal Infante actorazoapellido_paternoend
Desarrollo Basado en Pruebas
Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas perono explicamos especiacuteficamte la mecaacutenica del desarrollo basado en pruebas con un ejemploAhora veremos un ejemplo praacutectico de eacuteste desarrollo que le permitiraacute comprender elproceso de pensamiento que utiliza un desarrollador que se basa en pruebas
Cambio de Metodologiacutea
Lo que vamos a presentar a continuacioacuten representa un cambio demetodologiacutea y tren de pensamiento de lo que muchosdesarrolladores estaacuten acostumbrados
Acostumbrarse a probar primero no es sencillo pero vale la penaintentarlo
Apellidos en Espantildeol
En la lengua de Cervantes los apellidos tienen reglas un poco especiales Necesitamosimplementar un meacutetodo llamado apellidos que desplegaraacute los apellidos de la siguientemanera
Si hay apellido ambos apellidos Paterno Materno (separados por espacios)Si hay apellido paterno uacutenicamente PaternoSi no hay apellido paterno Materno
Como estamos desarrollando basados en nuestras pruebas primero tenemos que prepararuna prueba que falle y datos de prueba
Preparando Datos de Prueba
Para los datos de prueba antildeadimos a personasyml registros que cubran los casosadecuados
sin_appellidos id 3 nombre Maria desplegar Maria sexo Mujer
solo_apellido_paterno id 4
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
nombre Juan apellido_paterno Gomez
solo_apellido_materno id 5 nombre Pedro apellido_materno Paramo ambos_apellidos id 6 nombre Juan apellido_materno Torres apellido_paterno Mendoza
Coacutedigo de Prueba
Ahora que tenemos informacioacuten de prueba podemos crear nuestras pruebas Todaviacutea notenemos el coacutedigo pero sabemos lo que esperamos en los diferentes casos Asiacute queantildeadimos a nuestro archivo testunitpersona_testrb los meacutetodos de prueba quenecesitamos Comenzamos los meacutetodos con test para que la clase TestCase los ejecute
class PersonaTest lt TestUnitTestCase fixtures personas
def test_ambos_apellidos bien_nacido = personas(ambos_apellidos) assert_equal( bien_nacidoapellido_paterno+ +bien_nacidoapellido_materno bien_nacidoapellidos )
end
def test_solo_paterno persona = personas(solo_apellido_paterno) assert_equal( personaapellido_paterno personaapellidos ) end
def test_solo_materno persona = personas(solo_apellido_materno) assert_equal( personaapellido_materno personaapellidos ) end
def test_sin_apellidos persona = personas(sin_apellidos) assert_equal( personaapellidos ) end
end
Ahora podemos escribir un metodo cabo (un meacutetodo baacutesicamente vaciacuteo) que ataremoscon el coacutedigo adecuado maacutes tarde En nuestro archivo appmodelspersonarb
def apellidos end
La Prueba que Falla
Ahora cuando ejecutemos rake veremos que la uacutenica prueba que funciona es la desin_apellidos Funciona de pura casualidad porque la implementacioacuten de nuestro meacutetodoestaacute vacia
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
$ rake TEST=testunitpersona_testrb(in cygdrivechomedavidplaydirecciones)usrbinruby -Ilibtest usrlibrubygems18gemsrake-070librakerake_test_loaderrb testunitpersona_testrbLoaded suite usrlibrubygems18gemsrake-070librakerake_test_loaderStartedFFFFinished in 0611 seconds
1) Failuretest_ambos_apellidos(PersonaTest) [testunitpersona_testrb13]ltMendoza Torresgt expected but wasltgt
2) Failuretest_solo_materno(PersonaTest) [testunitpersona_testrb24]ltnilgt expected but wasltgt
3) Failuretest_solo_paterno(PersonaTest) [testunitpersona_testrb19]ltGomezgt expected but wasltgt
5 tests 5 assertions 3 failures 0 errorsrake abortedTest failures
(See full trace by running task with --trace)
Note que podemos ejecutar soacutelo una prueba simplementeantildeadiendo el nombre del archivo ruby que contiene su prueba
Pero lo importante que hay que tener en cuenta es que gracias a nuestras pruebas deunidad (que fueron creadas antes del coacutedigo basadas en el disentildeo) tenemos las siguientesventajas
Sabemos en todo momento queacute condiciones estaacuten cubiertas por nuestro coacutedigo (gracias a que losnombres de las pruebas son descriptivos)Tenemos una buena idea de queacute tanto avance hemos hecho en nuestro coacutedigo (por ejemplo en estemomento una de cuatro pruebas pasan)Otras personas pueden comenzar a integrar con nuestro meacutetodo apellido y nuestras pruebas manejanlas expectativas Esto es uacutetil en especial cuando usted trabaja en equipoUsted puede antildeadir el comando rake a un proceso nocturno para saber cuando hay cambios querompen las pruebas Cuando el coacutedigo cambie en una manera que no es compatible con el resto delsistema sus pruebas se romperaacuten y usted lo sabraacute antes de que se enteren sus usuariosCuando usted descubra alguacuten problema causado por liacutemites (valores fuera de lo comuacuten) puede antildeadirotra prueba al aacuterea adecuada y despueacutes correjir el error lo cual le proporciona una regresioacutenautomaacutetica
Implementando la funcioacuten
Finalmente podemos implementar la funcioacuten para los apellidos y continuar arreglandoproblemas hasta que nuestro comando de prueba muestre el resultado deseado
$ rake TEST=testunitpersona_testrb
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
5 tests 5 assertions 0 failures 0 errors
Y ahora si podemos decir que nuestro soporte de apellidos en espantildeol estaacute completo ytenemos cinco pruebas que lo demuestran
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
10 Teoriacutea de Objetos
ObjetosUn objeto es un tipo de datos que incorpora datos y coacutedigo (comportamiento) en un solopaquete Aacutentes de la era de la orientacion a objetos el coacutedigo y los datos eran dos cosasseparadas La orientacioacuten por objetos nos permite representar de un modo mucho maacutesconveniente al mundo real
iquestCoacutemo podemos modelar un objeto del mundo real con objetos de computadora Veamosun ejemplo
Ejemplo de un Objeto
Supongamos que estamos haciendo un juego acerca de un Acuario Para este juegoqueremos disentildear un Bonito Delfiacuten que va a brincar con el aro y jugar con la pelota peroqueremos en el futuro extender el juego para cualquier tipo de animal marino porque va aser una simulacioacuten bien picuda Tal como en el mundo real necesitamos comenzar con unobjeto llamado pescado (Para los amantes de la biologia Ya se que el delfiacuten es unmamiacutefero y no un pescado pero eacuteste es un ejemplo) El objeto pescado tiene sus datoscomo son alto largo peso y color Pero el pescado tambien puede hacer otras cosas comopor ejemplo nadar y sumergirse Entonces primero hacemos nuestro objeto pescado quetiene la siguiente forma
class Pescado
def Pescado
largo = 200
alto = 50
ancho = 50
peso = 350
end
def nadar
Codigo
end
def sumergirse
Codigo
end
end
Este coacutedigo le dice a la computadora Pescado es una clase (una clase es una definicioacuten deun objeto) Tiene los campos Largo Alto Ancho y Peso que son nuacutemeros de punto flotanteSus meacutetodos puacuteblicos (lo que todo mundo sabe que un pez puede hacer) son Nadar ySumergirse
Si esto parece complejo por favor sigan leyendo Muy pronto todo quedaraacute claro
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Ahora el Delfiacuten He decidido para este ejemplo que hay dos clases de delfines losentrenados y los salvajes (no entrenados) Asi que comencemos con el delfin entrenado
class Delfin lt Pescado
def tomar_aire
Codigo
end
def haz_ruido
Codigo
endend
Ahora bien este codigo le dice a la computadora El Delfin es una clase que heredade Pescado (o Un Delfin es un Pescado) Esto quiere decir que un Delfin puede hacertodo lo que un pescado puede hacer (tiene largo alto ancho peso y ademaacutes puedenadar y sumergirse) Entonces yo ya termineacute mi delfin y no tuve que implementar de nuevolas funciones para nadar y sumergirse que el pescado (y ahora tambien el delfin) puedehacer Ahora vamos a entrar en la materia del jueguito el delfin entrenado
class DelfinEntrenado lt Delfin
def juega_con_pelota(segundos)
Codigo
end
def brinca_el_aro(circunferencia)
Codigo
end
end
El DelfinEntrenado es una clase que hereda no de Pescado sino de Delfin (o Un delfiacutenentrenado sigue siendo un delfin pero) Al igual que la vez anterior un delfiacuten entrenadopuede hacer todo lo que un delfin puede hacer pero ademaacutes puede jugar con pelota ybrincar el aro
iquestPorqueacute hacer tres objetos nada maacutes para representar un delfiacuten Supongamos que ahoraquiero hacer un tiburoacuten
class Tiburon lt Pescado
def Tiburon
dientes = 200
bocon = true
come_hombres = false
end
def enojate
end
def come_persona(sabroso)
end
def espanta_visitantes
end
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
end
Gracias a la orientacioacuten a objetos ahora estoy re-utilizando el coacutedigo que useacute paraimplementar mi delfiacuten Ahora si mantildeana descubro que los pescados pueden nadar de unamanera especial o quiero hacer la simulacioacuten detallada y quiero hacer que hagan algo maacutescomo nacer y morirse todos los objetos de tipo pescado (tiburon delfiacuten delfiacuten entrenadopez dorado) van a nacer y morir igual que el pescado porque la implementacioacuten aplica altipo y a todos los tipos de la misma clase Ahora bien la jerarquiacutea de los objetos quehemos hecho es como sigue
Object
+----- Pescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
| +----- Tiburon
La jerarquiacutea de objetos es una especie de aacuterbol genealoacutegico que nos dice queacute objetos sederivan de otros objetos Como conceptualizamos esta jerarquiacutea Es de hecho bastantesencillo una vez que nos acostumbramos al aacuterbol Por ejemplo supongamos que queremossaber que interfaces soporta el Delfiacuten Entrenado Leemos todos los padres de lasiguiente manera Un Delfin ES UN Pescado que ES UN Objeto Esto quiere decir que unobjeto de la clase DelfinEntrenado soporta todas los metodos que objetos de la clase Delfiny de la clase Pescado
Tipos en Ruby
Los lenguajes orientados a objetos son tiacutepicamente de tipos fuertes o tipos deacutebiles
Un lenguaje de tipos fuertes revisa las asignaciones de valores en el programa para ver sison del mismo tipo antes de la ejecucioacuten (al tiempo de compilar) Un lenguaje de tiposdeacutebiles soacutelo revisa al tiempo de ejecucioacuten
La evaluacioacuten de tipos en ruby es un poco especial En ruby los tipos estaacuten definidos comolas capacidades de un objeto en vez de estar definidos arbitrariamente por naturaleza Enotros lenguajes esto se llama polimorfismo y lo consiguen mediante interfaces En Ruby lasinerfaces no son necesarias
Los rubyistas le llaman evaluacioacuten tipo Pato por el dicho en Ingleacutes que va Si caminacomo un pato y habla como un pato es un pato
Esto quiere decir que ruby intentaraacute ejecutar los meacutetodos de su objeto incluso cuando lostiacutepos sean diferentes En nuestro ejemplo anterior si le antildeadieramos al Tiburoacuten un meacutetodobrinca_el_aro
class Tiburon lt Pescado
def brinca_el_aro(circunferencia)
endend
Nuestro coacutedigo intentariacutea ejecutarlo incluso cuando fueacute disentildeado para un DelfiacutenEntrenado
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
11 Mapeo de Objetos a Relaciones
El Modelo Relacional vs El Modelo de Objetos
Las bases de datos SQL funcionan en modo relacional Esto quiere decir que utilizan unaMatriz de celdas para representar los datos En cada rengloacuten de una matriz existe unregistro individual Cada columna de la matriz representa uno de los valores de ese registroy tiene un tipo especiacutefico (numerico caracteres etcetera)
Tabla Personas
id Nombre Apellido Sexo Nacido En1 Elvis Presley M Nashville Tenesee2 Albert Einstein M Wuumlrttemberg Alemania
En el modelo relacional dos tablas pueden estar relacionadas a traves de una clave deidentidad entre otras cosas para poder representar valores repetidos o valores que nopertenecen a la naturaleza de lo que representa la tabla
Tabla Telefonos
id persona_id Pais Area Telefono1 2 52 777 334-23402 2 49 30 495-20303 1 1 615 394-2304
A traves de una de las columnas la tabla se relaciona
Sin embargo en el modelo de objetos los registros son instancias de un tipo de objeto Enel mundo de programacion orientada a objetos no hay diferencia innata entre un tipo que esparte del lenguaje (por ejemplo String) y un tipo que representa registros para la base dedatos (como Persona o Telefono) Ademaacutes en el modelo de objetos los tipos tienenreferencias directas
Personanombre Stringapellido StringsexoMasculino booleannacidoEn Stringtelefonos Telefono[]
pais Stringarea Stringtelefono String
Mapeo de objetos quiere decir crear una manera de convertir un diagrama de objetos paraque pueda ser automaacuteticamente mantenido por la base de datos Los sistemas de mapeo deobjetos tiacutepicamente saben como mapear los tipos innatos del lenguaje como string e intEstos tipos son especiales ya que son tipos de columnas en el mundo de las bases de datosrelacionales
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Aproximaciones al problema
Ahora que sabemos que el mapeo de objetos consiste en crear un mapa de los tipos deobjetos a las tablas columnas y tipos de datos podemos imaginar dos tipos deaproximaciones
De Objeto a Tabla- Utilizando este meacutetodo usted crea el objeto primero y despues antildeade metadatosal objeto (ya sea como comentarios con alguacuten archivo de configuracioacuten o con definiciones en el objetomismo) que especifican los nombres de archivos y el estilo de mapeo que se utilizaraacute
Este meacutetodo es preferido por los arquitectos de software ya que permite disentildear el modelo de objetosindependientemente del sistema que se utilice para guardar estos objetos
Aunque tiene la ventaja de que produce un disentildeo aparentemente maacutes limpio desde el punto de vistade los arquitectos el problema de estos disentildeos es que a los administradores de bases de datos no lesgusta porque los sistemas disentildeados asiacute resultan bastante lentos para accesar la base de datos
De Tabla a Objeto- Este meacutetodo es favorecido principalmente por los administradores de bases dedatos porque permite optimizar las tablas desde la fase de disentildeo Una implementacioacuten de este meacutetodoes la libreriacutea Hibernate (para Java y Net)
Aunque tiene la ventaja de que produce un disentildeo maacutes eficiente el problema de este meacutetodo es quepuede provocar que las definiciones semaacutenticas de los objetos parezcan arbitrarias
ActiveRecord
Ruby utiliza la libreriacutea ActiveRecord para mapear objetos a relaciones Sus clases heredande ActiveRecordBase Si usted ya ha seguido el tutorial acerca de coacutemo crear un moacuteduloMVC usted sabraacute que el meacutetodo que utiliza es el meacutetodo de tabla a objeto Sin embargousted puede cambiarle de nombre a las columnas y las tablas para crear un modelosemaacutentico maacutes claro
Definiendo nombres de columnas y tablas
En ActiveRecord los nombres de las columnas se pueden cambiar redefiniendo el meacutetodotable_name() del objeto de la manera siguiente
class Personas lt ActiveRecordBase def selftable_name() GENTES end end
Para cambiar nombres de columnas (y para hacer campos calculados) usted puedesimplemente definir meacutetodos para obtener y cambiar los valores en un patroacuten de fachada
class Personas lt ActiveRecordBase
def nombre() read_attribute(G_NOMBRE) end def nombre=(elnombre) write_attribute(G_NOMBRE elnombre) end def nombre_completo() nombre() + + apellido() end end
En este ejemplo el programador ha antildeadido una fachada para que la columna
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
G_NOMBRE responda a la propiedad nombre en el objeto Ademaacutes se ha antildeadido unapropiedad nombre_completo que formatea el nombre y apellido
Nota Si usted tiene control del disentildeo de base de datos asiacute comode los objetos es maacutes recomendable utilizar los nombres de lastablas y columnas de tal manera que coacutedigo de mapeo no seanecesario como lo vioacute en el tutorial de moacutedulo MVC
De esta manera su coacutedigo es maacutes sencillo de mantener
Definiendo Relaciones Entre Objetos
Vimos hace unos momentos que en el modelo relacional las relaciones entre dos tipos sonrepresentadas utilizando columnas relacionadas Ahora veremos este y varios otros tipos derelaciones
Relaciones Circulares
Las relaciones de pertenencia se representan con has_one o con has_many lo cual lepermite especificar que la relacioacuten es de uno a uno o de uno a muchos respectivamente Larelacioacuten opuesta se puede representar con belongs_to en la clase de destino El resultadose ve muy natural
class Persona lt ActiveRecordBase has_one detalle has_many telefonoend
class Detalle lt ActiveRecordBase belongs_to personaend
class Telefono lt ActiveRecordBase belongs_to personaend
Esto requiere que los siguientes nombres de columnas por defecto
Tabla Columnapersona iddetalles persona_idtelefonos persona_id
Por supuesto si es necesario usted puede cambiar los nombres de columnas por defectopor medio de modificar las opciones de belongs_to En el siguiente ejemplo cambiaremos elnombre de la columna que conecta a la persona en la clase de detalles
class Detalle lt ActiveRecordBase belongs_to persona foreign_key=gthumano_idend
Relaciones Circulares
La posibilidad de cambiar opciones en belongs_to permite entre otras cosas crearrelaciones circulares por ejemplo
class Persona lt ActiveRecordBase
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
belongs_to padre class_name=gtPersona foreign_key=gtpadre_id belongs_to madre class_name=gtPersona foreign_key=gtmadre_id
end
Asociaciones con tabla de Pivote
Un tipo de relaciones muy uacutetil en el mundo de SQL es la relacioacuten de muchos a muchos locual se logra con lo que se llama una tabla de pivote Supongamos que tenemos unarelacioacuten de Personas y Compantildeias Una persona puede ser cliente de varias compantildeiacuteas Ylas compantildeiacuteas pueden tener varios clientes La tabla de pivote es como sigue
companias_personas tipopersona_id intcompania_id int
Esta relacioacuten se puede representar con has_and_belongs_to_many de la manera siguiente
class Compania lt ActiveRecordBase has_and_belongs_to_many personasend
class Persona lt ActiveRecordBase has_and_belongs_to_many companiasend
Definiendo Jerarquiacuteas de Objetos y Herencia
Las jerarquiacuteas de objetos en sistemas de mapeo relacional se pueden representarautomaacuteticamente con una sola tabla Para lograrlo su tabla necesita tener un campollamado type que representaraacute el tipo del objeto y los campos de la jerarquiacutea completadeben estar definidos
Por ejemplo para representar los campos de la jerarquiacutea de objetos de nuestro ejemplo enla seccioacuten teoriacutea de objetos (una jerarquiacutea de pescado-gtTiburoacuten-gtDelfiacuten) podriacuteamos definiruna tabla como sigue
Tabla Pescados
nombre tipoid integertype varcharnombre varcharlargo integerancho integerancho integerpeso integerPropiedades del Delfiacuten
mamarias integerPropiedades del Tiburoacuten
come_hombres booleandientes integerbocon boolea
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Con este tipo de tabla ActiveRecord simplemente utilizaraacute los campos adecuados yregresaraacute el tipo adecuado dependiendo del campo type
Aacuterboles
Un aacuterbol es una representacioacuten donde los objetos tienen un soacutelo padre Para definir un aacuterbolen ActiveRecord utilizamos el coacutedigo acts_as_tree y definimos un campo llamado parent_idque permita valores nulos
Tabla Categorias
nombre tipoid integerparent_id integer NULLorden integernombre varchar
Con esto podemos definir la tabla e incluso podemos utilizar opciones para mantener elorden de los nodos hijos asiacute como para mantener una cuenta de hijos
class Categoria lt ActiveRecordBase acts_as_tree order =gt orden counter_cache =gt true end
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
12 Modificando el Moacutedulo Generado
Tenemos nuestro coacutedigo generado pero maacutes que nada esto fueacute para que nuestros usuarioslo vieran e hicieran algunas decisiones en cuanto a los campos que faltan etceacutetera Ahoraanalizaremos coacutemo funciona una vista para comenzar a hacer las modificaciones
Vistas en nuestro moacutedulo
Cuando generamos el moacutedulo de personas se generoacute un moacutedulo de controlador y variasvistas Repasemos lo que fueacute generado
appviewslayoutspersonasrhtml appviewspersonas_formrhtml appviewspersonaseditrhtml appviewspersonaslistrhtml appviewspersonasnewrhtml appviewspersonasshowrhtml
Recordemos que ruby utiliza convencioacuten en vez de configuracioacuten La vista para elmoacutedulo de personas siempre comenzaraacute en el esquema (layout) de personas Cuandovemos el texto dentro del archivo layoutspersonasrhtml veremos lo siguiente
lthtmlgtltheadgt lttitlegtPersonas lt= controlleraction_name gtlttitlegt lt= stylesheet_link_tag scaffold gtltheadgtltbodygt
ltp style=color greengtlt= flash[notice] gt
lt= content_for_layout gt
ltbodygtlthtmlgt
Un archivo RHTML es una combinacioacuten de HTML y ruby El coacutedigo ruby vive dentro de losdelimitadores lt gt o lt= gt si desea el resultado de una funcioacuten desplegado en el HTML
Veamos la uacuteltima parte del esquema de personas
lt= content_for_layout gt
Este comando le dice a rails que es aquiacute donde puede insertar el contenido del esquema Elesquema es como un formato general para este controlador
Despachando peticiones en Rails
El resultado de una peticion a nuestra aplicacioacuten depende del URL Cuando un usuario pide
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
httpmiservidorpersonalist Rails deduce que el controlador se llama persona y laaccioacuten dentro del controlador es list Asiacute que rails haraacute lo siguiente
Ejecutar el meacutetodo list en appcontrollerspersonas_controllerrb
Utilizar viewspersonaslistrhtml para evaluar la presentacioacuten de los resultados del meacutetodo listUna vez evaluados los resultados se ponen en una variable llamada content_for_layoutUtilizar viewslayoutspersonasrhtml como el esquema para desplegar los resultados del controladorEn alguacuten lugar de este esquema mostraremos la variable content_for_layout como vimos conanterioridad
Esto quiere decir que si usted desea cambiar de nombre al meacutetodo list para traducirlo alistar debe hacer lo siguiente
Cambiar de nombre al meacutetodo list en appcontrollerspersonas_controllerrb
Cambiar de nombre al archivo viewspersonaslistrhtml
Comandos con formularios
Gran parte de lo que hacemos en Rails es llenar formularios Los formularios en Rails soncompartidos utilizando el archivo _formrhtml Veamos una de las vistas que requieren elformulario appviewspersonaseditrhtml
lt= start_form_tag action =gt update id =gt persona gt lt= render partial =gt form gt lt= submit_tag Edit gtlt= end_form_tag gt
lt= link_to Show action =gt show id =gt persona gt |lt= link_to Back action =gt list gt
Note que el moacutedulo de edicion prepara la forma para que llame el comando update y despuesemite un contenido parcial llamado form Dentro del contenido parcial _formrhtmlpodemos ver lo siguiente
lt= error_messages_for persona gt
lt--[formpersona]--gt
ltpgtltamptlabel for=persona_nombregtNombreltlabelgt
lt= text_field persona nombre gtltpgt
(mas y mas campos)lt--[eoformpersona]--gt
Rails tiene comandos para todos los controles de forma del HTML y tien algunos controlesextra Cuando usted estudie el HTML generado podraacute hacer sus propios controlescomplejos
Parciales
Como lo hemos visto con el ejemplo de la forma los parciales son muy uacutetiles Un parcial esun fragmento de RHTML que usted puede re-utilizar en varios contextos ya sea en variosmeacutetodos en varios controladores o varias veces en la misma vista
Usted puede crear un parcial simplemente antildeadiendo el archivo (su nombre debe comenzarcon un simbolo de subrayado) y llamando el parcial con render partial=gtmiparcial (paraun archivo llamado _miparcialrhtml) Por defecto el parcial seraacute buscado dentro del
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
directorio de la vista actual pero usted puede cambiar el directorio simplementemencionandolo por ejemplo render partial=gtdirectoriomiparcial (en este caso elarchivo seriacutea appviewsdirectorio_miparcialrhtml)
El parcial tendra acceso a todas las variables de instancia del controlador y la vista que lollamoacute
Ideas para usos de parciales
Ademaacutes de las formas he aquiacute algunos ejemplos para uso de parciales
Navegacioacuten En vez de repetir un area de navegacion comun por cada pagina usted puede llamar unparcial para la navegacion comunCabecerasPieceras Usted puede crear cabeceras y pieceras una sola vez y simplemente llamarlasdentro de sus esquemasRegistros complejos dentro de tablas Si usted necesita desplegar muchos registros y el tipo deregistro es muy complejo (digamos que tiene muchas clases y javascript que debe de funcionarperfectamente) puede crear un parcial para desplegar un registro individual y llamar el parcial dentro delcicloCanastas para compras Es importante que la canasta que muestra la orden en un programa decomercio sea uniforme En vez de repetir el coacutedigo usted puede llamar un parcial
Cambiando el Listado
Ahora hay que hacer que el listado se vea mejor Primero veamos el coacutedigo generado en viewspersonaslistrhtml (o listar si ya le cambioacute el nombre)
Leyendo el coacutedigo generado descubriremos el uso de metadata en las columnas de registrosde ActiveRecord
lttablegt lttrgt lt for column in Personacontent_columns gt ltthgtlt columnhuman_name gtltthgt lt end gt lttrgt
lt for persona in personas gt lttrgt lt for column in Personacontent_columns gt lttdgtlt= h personasend(columnname) gtlttdgt lt end gt lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttdgtlt link_to Edit action =gt edit id =gt persona gtlttdgt lttdgtlt link_to Destroy action =gt destroy id =gt persona confirm =gt Are you sure gtlttdgt lttrgtlt end gtlttablegt
Note que el coacutedigo generado es dinaacutemico Cuando usted antildeade columnas a su objetoPersona la tabla tendraacute esas columnas tambien Tambieacuten note que para crear un enlace aalguna accioacuten sobre la persona en cada rengloacuten se utiliza el comando link_to
Evidentemente esto soacutelamente funciona para prototipos raacutepidos porque el dinamismo soacuteloaplica a las columnas y no a los meacutetodos calculados (como el campo calculado apellidos)
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Asiacute que cambiamos el listado para que sea maacutes estaacutetico
lt for persona in personas gt lttrgt lttdgtlt= h personanombre gtlttdgt lttdgtlt= h personaapellidos gtlttdgt (mas campos) lttdgtlt link_to Show action =gt show id =gt persona gtlttdgt lttrgt
Obviamente es aquiacute donde usted puede hacer el HTML tan artiacutestico como quieraHablaremos de HTML y CSS maacutes tarde pero esto le da una buena base para comprendercoacutemo funciona la generacioacuten de vistas en Rails y coacutemo usted puede cambiar el HTMLgenerado y planear su sitio Por ahora cambie la tabla para que tenga soacutelo los registros queusted desee
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
13 Buen Disentildeo con CSS
Con anterioridad mencionamos que Rails es parte del paradigma de disentildeo de aplicacionesde web 20 Esto quiere decir que el disentildeo de la paacutegina debe de seguir principios de disentildeode HTML modernos En este capiacutetulo veremos los principios e ideas que influencian eldisentildeo de Rails
Nota
Este no es un curso de CSS La idea es entenderporqueacute Rails y AJAX requieren de CSS y un disentildeolimpio Si no sabes CSS un buen lugar paracomenzar es el Tutorial de CSS de W3org
CSS - Hojas de Estilo de Cascada
Las hojas de estilo CSS le permiten separar el contenido de su paacutegina de la presentacioacuten dela misma Asimismo la capacidad de antildeadir maacutes de un estilo a su paacutegina web le permite
Cambiar su disentildeo visual sin cambiar su contenidoMantener su coacutedigo HTML y CSS limpio y claroAntildeadir y combinar elementos de disentildeo por referencia (sin incluirlos directamente en su archivo HTML)
Disentildeo Semaacutentico Rails y AJAX
Limpieza semaacutentica quiere decir claridad de propoacutesito dentro de nuestro coacutedigo Esto seobtiene
1 Manteniendo Nuestro coacutedigo HTML limpio y claro2 Antildeadiendo divisiones loacutegicas e identificadores de acuerdo con el propoacutesito de nuestra paacutegina
HTML Claro con CSS
Si la idea es crear tres paacuterrafos y un tiacutetulo el coacutedigo debe tener tres ltpgts y un lth3gt Antesde CSS los disentildeadores de HTML debiacutean poner muchos de sus paacuterrafos y tiacutetulos en tablaspara poderlos posicionar correctamente Un buen disentildeo semaacutentico evita usar tablas y otrasetiquetas cuyo uacutenico propoacutesito es forzar el estilo visual de la paacutegina Esto es porque lastablas obscurecen el propoacutesito del texto
Tablas y Disentildeo Semaacutentico
Antes del disentildeo semaacutentico las tablas se utilizabanmucho para posicionar su disentildeo con exactitud Unbuen disentildeo moderno soacutelo utiliza tablas paradesplegar datos tabulares como listas y hojas decaacutelculo (que era la razoacuten original de la existencia delas tablas en primer lugar)
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
Divisiones e Identificadores
Una vez que nuestro disentildeo de HTML estaacute limpio podemos antildeadir etiquetas de division(ltdivgt) y attributos identificadores (id=) Basados en estos atributos usted puede definiruna estructura que le permite a Rails AJAX y otras herramientas manipular su HTML dentrodel navegador Por ejemplo dentro de nuestra libreta de direcciones podemos hacer losiguiente
ltdiv id=persona_detalle_contenedorgt lth1 class=titulo_detallegtEditar Personalth1gt ltform class=forma_personagt ltlabel for=persona_nombregtNombreltlabelgt ltinput name=persona_nombregtltinputgt ltdivgt
Esta claridad nos permite utilizar funciones de AJAX para reemplazar el contenido depersona_detalle_contenedor con una vista de detalle una vez que el usuario guarde laforma Como las divisiones en la estructura son claras es mucho maacutes sencillo cambiar lapaacutegina existente
Muchos de los conceptos de Rails y otras ayudas de desarrollo para web 20 se basan encambiar secciones de HTML sin refrescar la paacutegina completa
Herramientas de HTML Visuales
Las herramientas visuales de HTML tienden a insertar tablas yotros elementos no requeridos Cuando evaluacutee una herramientaque genere HTML para su uso personal verifique si el HTMLgenerado es semaacutentico con CSS y no contiene tablas Eviteherramientas que no puedan generar CSS o respetar la estructurasemaacutentica de su paacutegina
La mejor manera de aprender coacutemo hacer estos disentildeos es ver otros disentildeos limpios yexaminar sus fuentes de HTML He aquiacute algunos ejemplos de buen disentildeo
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs
14 Utilizando Helpers
HelpersUn Helper es un moacutedulo que ayuda a tus vistas definiendo funciones que ayudan a que tusvistas sean maacutes que nada HTML y no contengan demasiado coacutedigo En un sistema demodelo MVC la idea es que la vista (la V de MVC) sea tan simple como sea posible
Rails viene con varios Helpers y tu aplicacioacuten tiene un application_helper para que tengasayudas especiacuteficas a tu aplicacioacuten Normalmente los helpers producen contenido para elHTML or Javascript de tu aplicacioacuten
Helpers incluidos en Rails
Alguacutenos de los helpers maacutes uacutetiles incluidos en Rails incluyen
AssetTagHelper Crea HTML para imaacutegenes Javascripts y CSS Te permite definir servidores de webestaacuteticos en tus archivos de configuracioacuten y el HTML cambiaraacute el servidor automaacuteticamente y definirpoacutelizas de expiracioacuten para tu contenidoFormHelper Contiene funciones para crear formas basadas en modelos de Rails incluyendo inputselecciones botones radiales aacutereas de teacutexto etceacuteteraJavaScriptHelper Contiene funciones que te permiten trabajar con Javascript maacutes faacutecilmente enparticular con prototype que estaacute incluido en RailsUrlHelper te permite hacer enlaces urls de mailto etceacutetera mencionando controladores directamente sintener que especificar los URLs