Universidad de Costa Rica Facultad de Ingeniería Escuela de Ingeniería Eléctrica IE – 0502 Proyecto Eléctrico Sistema computarizado para el manejo de inventario de bodega. Por: Francisco José Zumbado Alvarez Ciudad Universitaria Rodrigo Facio Diciembre del 2008 Sistema computarizado para el manejo de inventario de bodega. Por: Francisco José Zumbado Alvarez Sometido a la Escuela de Ingeniería Eléctrica de la Facultad de Ingeniería de la Universidad de Costa Rica como requisito parcial para optar por el grado de: BACHILLER EN INGENIERÍA ELÉCTRICA Aprobado por el Tribunal: _________________________________ Ing. Andrés Díaz Soto Profesor Guía _________________________________ _________________________________ Ing. Roberto Rodríguez Rodríguez Ing. José Freddy Rojas Chavarría Profesor lector Profesor lector DEDICATORIA A Dios por todo lo que me da en la vida. A mi familia y amigos por todo el apoyo y por ser una parte importante en mi vida. RECONOCIMIENTOS A los profesores que me ayudaron en la realización del proyecto, así como a demás personas que hicieron esto posible. INDICE GENERAL ÍNDICE DE FIGURAS.................................................................................................................. vii ÍNDICE DE CUADROS................................................................................................................ viii NOMENCLATURA....................................................................................................................... ix RESUMEN...................................................................................................................................... x CAPÍTULO 1: Introducción......................................................................................................... 1 1.1 Justificación................................................................................................................... 1 1.2 Objetivo general............................................................................................................ 2 1.3 Objetivos específicos..................................................................................................... 2 1.4 Metodología................................................................................................................... 2 CAPÍTULO 2: Desarrollo Teórico................................................................................................ 4 2.1 Bases de Datos............................................................................................................... 4 2.1.1 Objetivo de una base de datos....................................................................... 4 2.1.2 Tipos de bases de datos.................................................................................. 5 2.1.3 Abstracción de datos..................................................................................... 6 2.2 Modelos de Datos.......................................................................................................... 7 2.2.1 Modelos lógicos basados en objetos.............................................................. 7 2.2.2 Modelos lógicos basados en registros........................................................... 8 2.2.3 Modelos físicos............................................................................................... 9 2.3 Sistemas Gestores de bases de datos........................................................................... 9 2.4 Lenguajes de definición de datos (DDL)..................................................................... 10 2.5 Lenguajes de manipulación de datos (DML)............................................................. 10 2.6 Administrador de base de datos.................................................................................. 10 2.7 Usuarios de base de datos............................................................................................ 11 2.8 Lenguaje relacional SQL............................................................................................. 11 2.8.1 Estructura básica.......................................................................................... 12 2.8.2 Tuplas duplicadas.......................................................................................... 13 2.8.3 Operaciones de conjuntos............................................................................. 13 2.8.4 Operadores agregados................................................................................... 13 2.8.5 HAVING.......................................................................................................... 14 2.8.6 Subconsultas................................................................................................... 14 2.8.7 Definición de datos......................................................................................... 14 2.8.8 Tipos de datos................................................................................................. 14 2.8.9 Índices............................................................................................................. 15 2.8.10 Vistas............................................................................................................. 15 2.8.11 Eliminación de tablas, índices y vistas....................................................... 16 2.8.12 Manipulación de datos................................................................................ 16 2.8.13 Normalización de bases de datos................................................................ 17 2.9 El lenguaje de programación Ruby............................................................................. 19 2.9.1 Filosofía del lenguaje..................................................................................... 20 2.9.2 Semántica....................................................................................................... 20 2.9.3 Sintaxis........................................................................................................... 20 2.9.4 Metaprogramación........................................................................................ 21 2.10 Modelo Vista Controlador......................................................................................... 21 2.10.1 Descripción del patrón................................................................................ 22 2.11 Ruby on Rails............................................................................................................... 22 2.11.1 Principios Fundamentales.......................................................................... 23 2.11.2 El Modelo Vista Controlador de Ruby on Rails......................................... 23 2.11.3 Soporte de bases de datos............................................................................. 24 CAPÍTULO 3: Estructura general del sistema de inventario de bodega.................................. 26 3.1 Estructura de la aplicación y aspectos generales........................................................ 26 3.2 Base de datos.................................................................................................................. 26 3.3 Modelo............................................................................................................................ 27 3.4 Controlador.................................................................................................................... 27 3.5 Vista................................................................................................................................ 28 3.6 Archivos de configuración............................................................................................ 29 3.7 Otras carpetas............................................................................................................... 30 3.8 Plugins utilizados.......................................................................................................... 31 CAPÍTULO 4: Diseño de la base de datos.................................................................................. 33 4.1 Base de datos para activos de bodega......................................................................... 33 4.2 Base de datos para manejo de usuarios...................................................................... 35 CAPÍTULO 5: Modelo.................................................................................................................. 40 5.1 Modelos del sistema de activos..................................................................................... 40 5.2 Modelos del sistema de autenticación de usuarios.................................................... 42 CAPÍTULO 6: Controladores...................................................................................................... 44 6.1 Controladores de sistema de activos........................................................................... 44 6.2 Controladores de sistema de autenticación de usuarios........................................... 49 CAPÍTULO 7: Vistas.................................................................................................................... 55 7.1 Vistas del sistema de manejo de activos..................................................................... 55 7.2 Vistas del sistema de autenticación de usuarios........................................................ 61 7.3 Plantilla genérica para vistas ..................................................................................... 67 CAPÍTULO 8: Traducciones....................................................................................................... 71 CAPÍTULO 9: Pruebas................................................................................................................ 74 9.1 Pruebas de validación................................................................................................. 74 9.2 Búsquedas.................................................................................................................... 76 CAPÍTULO 10: Conclusiones y recomendaciones..................................................................... 78 10.1 Conclusiones................................................................................................................ 78 10.2 Recomendaciones........................................................................................................ 79 BIBLIOGRAFÍA............................................................................................................................ 80 INDICE DE FIGURAS Figura 1. Interrelación entre niveles de abstracción de datos..................................................... 7 Figura 2. Diagrama E­R para el sistema de manejo de activos.................................................. 35 Figura 3. Vista para creación de activo......................................................................................... 57 Figura 4. Vista para edición de activo........................................................................................... 59 Figura 5. Vista de listado de activos............................................................................................... 61 Figura 6. Vista de listado de usuarios............................................................................................ 63 Figura 7. Vista de edición de contraseña....................................................................................... 64 Figura 8. Vista de listado de roles asignados................................................................................ 65 Figura 9. Vista de listado de roles no asignados............................................................................ 66 Figura 10. Vista final del sistema para un usuario conectado..................................................... 69 Figura 11. Vista final del sistema para un usuario desconectado................................................ 70 Figura 12. Prueba de validación de placa existente...................................................................... 74 Figura 13. Prueba de validación de número de serie en blanco.................................................. 75 Figura 14. Prueba de validación de placa en blanco.................................................................... 75 Figura 15. Prueba de validación de placa no numeral................................................................. 76 Figura 16. Prueba de búsqueda de activo por número de serie. ................................................. 76 Figura 17. Prueba de búsqueda de activo por placa.................................................................... 77 INDICE DE CUADROS Cuadro 1. Activos en nivel de normalización 0............................................................................. 33 Cuadro 2. Activos en nivel de normalización 1............................................................................. 33 Cuadro 3. Activos en nivel de normalización 2............................................................................. 34 Cuadro 4. Estados en nivel de normalización 2............................................................................ 34 Cuadro 5. Marcas en nivel de normalización 2............................................................................. 34 Cuadro 6. Modelos en nivel de normalización 3........................................................................... 34 Cuadro 7. Marcas en nivel de normalización 3............................................................................. 35 Cuadro 8. Tabla de roles................................................................................................................. 37 Cuadro 9. Tabla de permisos.......................................................................................................... 37 NOMENCLATURA ROR:Ruby on Rails. CSS:Cascading Style Sheet. Hojas de estilo en cascada, formato utilizado para describir el formato de páginas escritas en HTML o XML. XML: Extensible Markup Language. Metalenguaje extensible de etiquetas utilizado en desarrollo web. HTML: Hyper Text Markup Language. Lenguaje de marcado predominante en el desarrollo de páginas erb. RTML: Real Time Markup Language. Lenguaje propietario de Yahoo utilizado para describir páginas web para la aplicaciopn Yahoo! Store Editor. JavaScript: Lenguaje de programación interpretado utilizado principalmente en páginas web. Framework:Estructura de soporte que permite desarrollar proyectos de software utilizando una metodología de desarrollo específica. RESUMEN Este proyecto tiene como objetivo desarrollar una aplicación que permita almacenar y manejar la información de los equipos existentes en el inventario de la bodega de la escuela de Ingeniería Eléctrica de la Universidad de Costa Rica. Se utilizó el gestor de base de datos PostgreSQL debido a su buena reputación y estabilidad en el manejo de las bases de datos. Para desarrollar la aplicación web se utilizó el lenguaje Ruby mediante un framework de desarrollo denominado Ruby on Rails. La estructura de la aplicación como de cualquier aplicación de Ruby on Rails, sigue el paradigma Modelo­vista­controlador. Este permite separar la aplicación en un modelo que realiza la comunicación con la base de datos, una vista que maneja la parte visible para el usuario, es decir las páginas web propiamente y un controlador encargado de interpretar y ejecutar las acciones realizadas por el usuario y permite enlazar el modelo y las vistas de la aplicación. La aplicación se dividió en una base de datos para manejo de los activos de bodega, en la cual se almacenan los diferentes activos clasificados por marca, modelo, estado y con su respectivo número de placa y serial, y una base de datos para el almacenamiento de la información de los usuarios y las sesiones de estos. De esta manera se añadió a la aplicación un sistema de autenticación de usuarios con diferentes permisos para estos, según sean usuarios normales que únicamente puedan consultar la información o usuarios administradores que pueden modificar la información. Finalmente la aplicación desarrollada cumple con los objetivos planteados y se determinó que es adecuada para cumplir la función deseada de almacenar la información de el inventario de la bodega de la escuela al mismo tiempo que permite a los usuarios consultar y modificar información de manera remota a través de Internet. CAPÍTULO 1: Introducción. 1.1 Justificación. En la actualidad vivimos en un mundo en el cual la eficiencia es un factor determinante en todos los campos, y la búsqueda de esta ha llevado al desarrollo tecnológico con equipos que son capaces de realizar tareas que antes debían realizarse completamente por personas, lo cual conllevaba a muchos errores humanos y que además requerían de mucho tiempo para llevarse a cabo. El desarrollo de la computación ha permitido ampliar de gran forma las funcionalidades que se le pueden dar a los avances tecnológicos, ya que ha permitido un procesamiento rápido y eficaz de la información. El desarrollo de bases de datos para llevar registros informáticos es una de las cosas que más ha contribuido en el manejo de la información, lo que las ha llevado a expandirse y popularizarse en muchos campos, convirtiéndolas en una herramienta indispensable a la hora de competir. El almacenamiento de la información ha sido un aspecto muy importante, sin embargo este almacenamiento no serviría de mucho si no se tuviera un adecuado acceso a la información. Las redes de computadoras han sido fundamentales en este aspecto, permitiendo a usuarios acceder a información en otro lado del mundo desde una computadora, a través de la Internet o de redes privadas de información. Este proyecto pretende utilizar estas herramientas que nos presenta la tecnología actual para crear un sistema de almacenamiento computarizado para el inventario de la bodega de la Escuela de Ingeniería Eléctrica de la Universidad de Costa Rica, mediante una base de datos manejada por medio de una interfaz web, que permita a los funcionarios de la bodega llevar un registro más adecuado de los equipos y materiales presentes en la bodega y que a la vez permita a los estudiantes y profesores acceder a este inventario de forma remota para comprobar la existencia y 1 disponibilidad de materiales. 1.2 Objetivo general. ● Crear un sistema computarizado de base de datos que permita almacenar, consultar y modificar el inventario de la bodega de la Escuela de Ingeniería Eléctrica de la Universidad de Costa Rica, permitiendo al usuario un manejo simple mediante una interfaz web. 1.3 Objetivos específicos. ● Diseñar e implementar una base de datos que permita describir de manera óptima los activos de la bodega de la Escuela de Ingeniería Eléctrica de la Universidad de Costa Rica. ● Seleccionar el gestor de base de datos, lenguajes de programación y herramientas que se utilizarán para el desarrollo de la aplicación, utilizando criterios tales como rendimiento, flexibilidad y seguridad, entre otros. ● Desarrollar una aplicación web que permita hacer consultas y administrar la base de datos. ● Crear un sistema de control de acceso para restringir el acceso a algunas funciones de la base de datos asignando ciertos permisos a los usuarios. 1.4 Metodología. ● Se investiga sobre bases de datos de licencia libre y los diferentes gestores para determinar el gestor que más se adecúa a las necesidades y requerimientos del proyecto. ● Se investiga sobre la teoría de bases de datos para utilizarla en el diseño de la base de datos a construir. ● Se realiza una entrevista con el encargado de la bodega para recolectar información sobre los diferentes aspectos que debe considerar la base de datos para el inventario. ● Se realiza una entrevista con el profesor tutor Jose Freddy Rojas para obtener una guía en el diseño de la base de datos. ● Se diseña la base de datos, tomando en cuenta la información recopilada y utilizando 2 técnicas de diseño adecuadas. ● Se investiga sobre los lenguajes más adecuados que permitan crear una interfaz web para la base de datos diseñada, para escoger el lenguaje más conveniente. ● Se investiga sobre el lenguaje escogido y sus diferentes herramientas que permitan el desarrollo del proyecto. ● Se desarrolla una interfaz web para la base de datos, que permita al usuario interactuar con esta de manera. ● Se realizan pruebas a la aplicación para detectar y corregir fallas en el sistema y garantizar el buen funcionamiento. ● A lo largo de todo el proyecto se desarrolla la documentación escrita de este. 3 CAPÍTULO 2: Desarrollo Teórico. 2.1 Bases de Datos [1][2]. Las bases de datos o bancos de datos son conjuntos de datos que forman parte de un mismo contexto y son almacenados de una forma ordenada para su utilización posterior. Una biblioteca es un ejemplo de base de datos, donde la mayoría de los datos son documentos impresos y textos. El desarrollo tecnológico y en especial de la computación ha contribuido a desarrollar nuevas y más eficientes formas de almacenar bases de datos. Los sistemas de bases de datos están diseñados para poder almacenar grandes cantidades de datos, lo que implica la creación de estructuras adecuadas para almacenar la información así como así como de mecanismos para la gestión de esta. Otro punto importante en las bases de datos es que deben ser los suficientemente seguras para garantizar el almacenamiento confiable de datos y no permitir manipulación por parte de personas no autorizadas. 2.1.1 Objetivo de una base de datos. Los sistemas de bases de datos son creados para evitar diversos problemas en el almacenamiento de datos como los siguientes: ● Redundancia e inconsistencia de los datos: La redundancia puede aumentar el costo de almacenamiento y acceso, además se puede presentar el caso de que diferentes copias de los datos no concuerden por lo que se presenta una inconsistencia en los datos, por ejemplo si para un cliente de un banco se presentan varias direcciones de domicilio. ● Dificultad de acceso a los datos: Cuando la cantidad de datos es considerable, se puede querer encontrar los datos que cumplan ciertas características, lo cual se puede complicar si no se utilizan métodos adecuados ya que sería necesario filtrar esta información de manera 4 manual o bien crear una aplicación nueva que haga este trabajo. ● Aislamiento de datos: La diferencia de formatos y de lugares de almacenamiento puede provocar que sea difícil obtener todos los datos necesarios. ● Anomalías del acceso concurrente: En sistemas de almacenamiento de datos que permiten a varios usuarios acceder y actualizar la información al mismo tiempo, pueden ocurrir anomalías a la hora de hacer esta actualización de los datos, por ejemplo si se modifica un dato al mismo tiempo por 2 usuarios, el resultado final puede ser incorrecto. ● Problemas de Seguridad: El acceso a la información debe ser restringido a ciertos usuarios de manera que otros no autorizados sean bloqueados y se de un acceso seguro a la información. ● Problemas de Integridad: El sistema de almacenamiento de datos debe ser capaz de evitar que algunos datos obtengan ciertos valores que se considerarían erróneos, para de esta manera lograr mantener la integridad de los datos. Todos estos problemas han llevado a la creación de sistemas gestores de bases de datos para evitar que se presenten dichos problemas, y así lograr un almacenamiento de los datos más eficiente. 2.1.2 Tipos de bases de datos. Las bases de datos se pueden clasificar de diferentes formas: Según la variabilidad de los datos: ● Estáticas: Son bases de datos de solo lectura que puede utilizarse para almacenar datos históricos donde los datos no deben variar en el tiempo. ● Dinámicas: En estas bases de datos la información almacenada es modificada a través del tiempo, de manera que se le agregan nuevos datos y otros son actualizados, además por 5 supuesto de operaciones de consulta. Según su contenido: ● Bibliográficas: Contienen únicamente los datos para la localización de la fuente primaria. Por ejemplo la base de datos de una biblioteca que contiene los datos de los textos y en que sección se puede localizar pero no contiene el documento en sí. ● De texto completo: Almacenan fuentes primarias en su totalidad por ejemplo todo contenido de diferentes ediciones de libros y revistas. ● Directorios: Un ejemplo son las guías telefónicas. ● De información biológica: Almacenan información de datos biológicos como secuencias de nucleótidos y proteínas. 2.1.3 Abstracción de datos. La abstracción de datos se presenta en 3 niveles distintos: ● Nivel físico: Este nivel es el más bajo y describe la forma en que los datos son almacenados físicamente. ● Nivel conceptual: El nivel conceptual describe cuales datos van a ser almacenados realmente y cual es la relación entre estos datos. En este nivel trabaja el administrador de la base de datos que es el que decide la información que se almacenará. ● Nivel de Visión: El nivel de visión muestra solo una parte de la base de datos que puede ser importante según el usuario que acceda a los datos. Desde este nivel pueden haber varios puntos de vista dependiendo del usuario. La siguiente figura nos muestra la interrelación entre estos niveles de abstracción: 6 Vista 1 Vista 2 Vista n Nivel Conceptual Nivel físico Figura 1. Interrelación entre niveles de abstracción de datos. 2.2 Modelos de Datos [1]. Un modelo de datos es un grupo de herramientas conceptuales que se utilizan para describir los datos, las relaciones entre ellos, la semántica y las restricciones de consistencia. Se dividen en: modelos lógicos basados en objetos, modelos lógicos basados en registros y modelos físicos. 2.2.1 Modelos lógicos basados en objetos. Los modelos lógicos basados en objetos se utilizan para describir datos en el nivel conceptual y el nivel de visión. Estos modelos permiten una estructuración flexible, además que permiten especificar restricciones de los datos explícitamente. Existen varios modelos de este tipo, sin embargo los más representativos son el modelo entidad­relación y el modelo orientado a objetos. El modelo entidad­relación: En este modelo se definen entidades que son objetos que se distinguen de otros objetos por ciertos atributos específicos que poseen. Además se describen las relaciones que presentan diferentes entidades. El modelo presenta además las restricciones a las que se deben ajustar la base de datos como por ejemplo la cantidad de entidades a la que se puede ajustar otra entidad por medio de un conjunto de relación. 7 La estructura de un modelo entidad ­relación se puede describir gráficamente por medio de diagramas E­R, los cuales presentan las diferente entidades y su relación entre ellas así como los atributos de cada entidad. El modelo orientado a objetos: Este modelo se basa en una colección de objetos los cuales contienen valores almacenados en variables. Estos valores se consideran a la vez objetos, por lo que se tienen objetos que contienen otros objetos que operan sobre el objeto. Además el objeto se componen de códigos que se denominan métodos y que operan también sobre el objeto. La diferencia con el modelo entidad­relación es que en el modelo orientado a objetos cada objeto posee su propia identidad sin importar los valores que contiene. 2.2.2 Modelos lógicos basados en registros. Los modelos basados en registros describen datos en el nivel conceptual y físico. Estos modelos se utilizan para especificar la estructura lógica de la base de datos, además de proporcionar una descripción a un nivel más alto de la implementación. Este modelo trabaja con registros fijos en el nivel físico, los cuales poseen un tipo fijo y una longitud también fija. Estos modelos no poseen mecanismos para la representación directa del código en la base de datos, sino que se apoyan en otros lenguajes separados que se asocian al modelo para realizar las consultas y actualizaciones a la base de datos. Los modelos más importantes basados en registros son el modelo relacional, el de red y el modelo jerárquico. El primero de ellos es el más aceptado. Modelo relacional: Este modelo presenta los datos e la relación entre ellos en una colección de tablas. Cada tabla presenta un número de columnas determinado. Modelo de red: En el modelo de red los datos se presentan mediante colecciones de registros, y las relaciones se presentan mediante enlaces, que se pueden ver como punteros. 8 Modelo jerárquico: El modelo jerárquico es parecido al modelo de red pues los datos y sus relaciones son presentados en registros y enlaces. La diferencia es que en este modelo los registros se ordenan como colecciones de árboles. La diferencia entre los modelos relacionales y los de red y jerárquicos radica en que en los dos últimos se utilizan punteros o enlaces, mientras que en el relacional se conectan estos registros mediante los valores que contienen. 2.2.3 Modelos físicos. Estos modelos se utilizan para describir los datos en el nivel más bajo. Los más conocidos son el modelo unificador y el de memoria de los elementos. 2.3 Sistemas Gestores de bases de datos [1][3]. Un sistema gestor de base de datos (SGBD), es una aplicación de software específica que está dedicada a funcionar como una interfaz entre la base de datos, el usuario y los programas que utilizan dicha información. Estos permiten a los usuarios acceder modificar los archivos que contienen la información, presentando esta información al usuario a través de diversos niveles de abstracción de manera que la interacción con el sistema sea lo más simple posible para el usuario. El propósito final de los SGBDs es el de manejar de forma sencilla y ordenada conjuntos de datos relevantes para brindar un buen manejo de estos. Un SGBD cumple las siguientes funciones: ● Interactuar con el gestor de archivos: Los datos se almacenan en el disco duro utilizando el sistema de archivos propio del sistema operativo utilizado. El SGBD es el encarado de transformar las sentencias de almacenamiento de los datos al formato propio del sistema de archivos de bajo nivel. ● Implantación de la integridad: El SGBD se encarga de hacer cumplir las restricciones que 9 pueden tener ciertos datos. El administrador de la base de datos es el encargado de especificar estas restricciones. ● Implantación de la seguridad: Se deben cumplir los requisitos de seguridad de la base de datos para evitar accesos no autorizados a esta por parte de usuarios restringidos. ● Copia de seguridad y recuperación: El SGBD debe detectar fallos en la base de datos como problemas con el disco de almacenamiento y debe ser capaz de crear copias de seguridad para una vez que se presente un fallo poder recuperar dicha información. ● Control de concurrencia: Se debe controlar la interacción de varios usuarios que actualizan la base de datos simultáneamente, para que se conserve la consistencia de los datos. 2.4 Lenguajes de definición de datos (DDL) [1]. Un lenguaje de definición de datos especifica el esquema de una base de datos por medio de un conjunto de sentencias. El resultado de la compilación de estas sentencias es un conjunto de tablas almacenadas en un archivo llamado “diccionario de datos”. 2.5 Lenguajes de manipulación de datos (DML) [1]. Un lenguaje de manipulación de datos permite a los usuarios acceder y manipular los datos según su organización. Pueden ser de dos tipos: ● Procedimentales: Estos requieren que es usuario especifique los datos que son necesitados y de que manera obtenerlos. ● No procedimentales: El usuario debe especificar los datos necesitados pero no como obtenerlos. 2.6 Administrador de base de datos [1]. Un administrador de bases de datos es la persona que tiene el control central de los datos y 10 de los programas que acceden a la base de datos o en inglés database administrator (DBA). Los administradores de bases de datos tienen varias funciones entre las que se encuentran: ● Definir el esquema. ● Definir la estructura de almacenamiento y el método de acceso. ● Modificar el esquema y la organización física. ● Conceder autorización de acceso a los usuarios. ● Especificar las restricciones de integridad de los datos. 2.7 Usuarios de base de datos [1]. Existen cuatro tipos distintos de usuarios de base de datos que se diferencian por la forma en que interactúan con el sistema: ● Programadores de aplicaciones: Son usuarios que crean aplicaciones para interactuar con el sistema por medio de sentencias DML incorporadas en un lenguaje principal. ● Usuarios sofisticados: Son usuarios que interaccionan con el sistema sin escribir programas. Únicamente hacen sus consultas en el lenguaje de consultas de la base de datos. ● Usuarios especializados: Son usuarios que escriben aplicaciones de bases de datos especializadas que no encajan en el marco tradicional de procesamiento de datos. ● Usuarios ingenuos: Son usuarios que interactúan con el sistema utilizando los programas de aplicación creados. Como un usuario de un cajero automático. 2.8 Lenguaje relacional SQL [1] [8]. El lenguaje SQL fue desarrollado originalmente por la empresa IBM al inicio de la decada de los setenta. Su nombre significa lenguaje de consulta estructurado, del inglés Structured Query Language. Este lenguaje se ha establecido como el lenguaje relacional de base de datos estándar. Posee varias partes: 11 ● Lenguaje de definición de datos (DDL): Proporciona órdenes que definen los esquemas de relación. Además eliminan relaciones, crean índices y modifican los esquemas. ● Lenguaje De manipulación de datos (DML): El SQL incluye un sistema de consultas basado en álgebra relacional y cálculo relacional de tuplas. Además se pueden insertar, eliminar y modificar tuplas en la base de datos. ● Definición de vistas: Se incluyen órdenes para definir vistas. ● Autorización: Se pueden definir permisos de acceso a diferentes relaciones y vistas. ● Integridad: Se incluyen órdenes para especificar restricciones de integridad complejas. ● Control de transacciones: Incluye órdenes para especificar el inicio y el final de las transacciones. Además algunas implementaciones permiten bloqueo de datos para control de concurrencia. 2.8.1 Estructura básica. Una expresión SQL consta de tres cláusulas básicas: select, from y where. ● Select: Esta operación se usa para listar los atributos que se desean en el resultado de la consulta. ● From: Esta cláusula lista las relaciones que se van a examinar en la ecaluación de la expresión. ● Where: Implica los atributos de las relaciones que se especificaron con la cláusula from. Una consulta típica en SQL presenta la siguiente estructura: select A1, A2, ..., An from r1, r2, ...,rm where P Donde los Ai representan atributos mientras que los ir representan relaciones. Además P es un predicado. Si se omite la cláusula where se da por un hecho que el predicado P es verdadero. Para 12 indicar todos los atributos se puede sustituir los Ai por un asterisco (*), esto seleccionará todos los atributos de las relaciones ri . 2.8.2 Tuplas duplicadas. El lenguaje SQL así como casi todos los lenguajes de consulta comerciales permiten duplicados en las relaciones. Hay veces que queremos eliminar estos duplicados, para esto se utiliza el la palabra distinct despúes de la frase select, de la siguiente forma: select distinct X from Y De manera contraria se utiliza la palabra all para evitar que se eliminen los duplicados. 2.8.3 Operaciones de conjuntos. select all X from Y Las operaciones de conjuntos unión, intersección y diferencia son incluidas por el lenguaje SQL. Para la operación de unión se utiliza la sentencia union, para la operación de intersección se utiliza intersect, mientras que para la operación de diferencia se utiliza la palabra minus. La operación union elimina las tuplas duplicadas automáticamente. Para mostrar los duplicados debemos escribir union all. La operación union es parte del estándar SQL, sin embargo varios productos no las soportan. Mientras tanto las operaciones intersect y minus no se incluyen en el estándar. 2.8.4 Operadores agregados. SQL presenta operadores agregados que utilizan como argumento un atributo. El valor del operador agregado se calcula sobre todos los elementos de la columna especificada o de ser especificados grupos se calculará sobre los valores de cada grupo. 13 ● AVG: Calcula el costo promedio. ● COUNT: Hace una cuenta de los artículos que pertenecen a la tabla. ● SUM: Calcula la suma de todos los valores. ● MIN: Calcula el valor mínimo. ● MAX: Calcula el valor máximo. 2.8.5 HAVING. La cláusula having es similar a la cláusula where, es utilizada para considerar solo grupos que cumplan un requisito específico. Esta cláusula utiliza expresiones que contengan funciones agregadas. Toda función que involucre expresiones con funciones agregadas debe ir en esta cláusula. 2.8.6 Subconsultas. Las cláusulas having y where permiten el uso de subconsultas en cualquier lugar en donde se espere un valor. El valor de la subconsulta debe derivar de la subconsulta previa, esta propiedad amplia el poder expresivo de SQL. Una subconsulta puede ser de la siguiente forma: select X from Y where Z > (select Z from Y where W= 'frase') 2.8.7 Definición de datos. SQL posee varias sentencias utilizadas para definir datos. Para definir relaciones se usa el comando create table que crea una tabla con los atribuutos especificados. La sintaxis de este comando es la siguiente: create table nombre de la tabla (atributo 1, tipo del atributo 1 [,atributo 2, tipo del atributo 2] [,...]); 14 2.8.8 Tipos de datos. SQL soporta varios tipos de datos. Los tipos más importantes y utilizados se muestran a continuación: ● INTEGER: Es un entero binario de 31 bits de precisión con signo de palabra completa. ● SMALLINT: Un entero binario con signo de media palabra de 15 bits de precisión. ● DECIMAL (p[,q]): Es un número decimal con signo que presenta p digitos de precisión y además contiene q digitos a la derecha del punto decimal. ● FLOAT: Número de doble palabra con signo y con coma flotante. ● CHAR(n): Es una cadena de caracteres con una longitud fija de n caracteres. ● VARCHAR (n): Es una cadena de caracteres con una longitud variable de un valor máximo de n caracteres. 2.8.9 Índices. Los índices nos permiten realizar de una forma más rápida el acceso a una relación, se puede comparar con un índice de un libro que nos permite encontrar de manera más rápida la ubicación de la información que necesitamos. El índice evita que el sistema de archivos lea toda la tabla para localizar la información deseada, y permite al sistema de base de datos chequear el valor del índice primero para ver si este le permite encontrar de forma más rápida la información. Es recomendado crear índices en las tablas para hacer más eficiente el acceso a la información. Para crear un índice se utiliza el comando create index. La sintaxis utilizada es la siguiente: create index nombre_del_indice on nombre_de_la _tabla (nombre_del_atributo); El único cambio que puede percibir un usuario con la utilización de índices es el incremento 15 en la velocidad a la hora de buscar información. 2.8.10 Vistas. Una vista es una tabla virtual, es decir una tabla que no existe físicamente en la base de datos pero que el usuario la puede ver como si existiese. La vista no tiene datos propios almacenados, sino que el sistema guarda la definición de la vista que permite saber cuales son las tablas base que presentan la información que la vista necesita. El comando utilizado para crear vistas es create vie. La sintaxis que se utiliza es: create view nombre_de_la _vista as sentencia_select Sentecia _select se refiere a una instrucción select que permite adquirir la información para crear la tabla de la vista. 2.8.11 Eliminación de tablas, índices y vistas. Para eliminar tablas se utiliza la instrucción drop table, con ls siguiente sintaxis: drop table nombre_de_la_tabla Para la eliminación de indices se utiliza el comando drop index, la sintaxis es la siguiente: drop index nombre_del_índice Para eliminar vistas se utiliza el comando drop view, con la siguiente sintaxis: drop view nombre_de_la_vista 2.8.12 Manipulación de datos. Existen comandos para manipulación de datos que permiten insertar, actualizar y borrar los datos en cada tabla. Para insertar tuplas a las tablas se utiliza el comando insert into, que presenta la siguiente sintaxis: insert into nombre_de_la_tabla (nombre_atributo_1 16 [, nombre_atributo_2] [,...]) values (valor_atributo_1 [, valor_atributo_2][, ...]); Para borrar una tupla se utiliza el comando delete from, con la siguiente sintaxis: delete from nombre_de_la_tabla where condición; También se pueden realizar cambios en un valor anteriormente ingresado, para lo cual se utiliza el comando update, que presenta la siguiente sintaxis: update nombre_de_la_tabla set nombre_atributo_1 = valor_1 [,...[,nombre_atributo_k = valor_k]] where condición; 2.8.13 Normalización de bases de datos [4]. La normalización de bases de datos es un proceso utilizado en el diseño de bases de datos relacionales, mediante el cual se pueden evitar ciertos problemas en el manejo de los datos. En la normalización se transforman los datos en estructuras más pequeñas de manera que sean más simples, más estables y más fáciles de mantener. Además hace más fáciles de entender las estructuras de datos para las personas . Otra de sus ventajas consiste en que se reduce el espacio de almacenamiento porque se evitan datos duplicados. La normalización permite básicamente evitar redundancia en los datos almacenados, además de problemas en la actualización de estos datos. Permite de esta forma garantizar la integridad de los datos. En el modelo relacional de bases de datos, las relaciones se suelen ver como tablas, siempre y cuando cumplan ciertos requisitos como que todas las columnas deben tener un nombre único. 17 Las filas de la tabla o “tuplas” no pueden estar repetidas, es decir, no se permiten datos duplicados. Además en una columna todos los datos deben ser del mismo tipo. Formas normales: Las formas normales son los diferentes niveles de normalización que se pueden alcanzar al realizar un diseño de una base de datos. Existen 5 formas normales, sin embargo las primeras 3 formas son suficientes para las necesidades de la mayoría de bases de datos . Inclusive se considera en algunos casos inadecuado llevar una base de datos a un nivel mayor al necesario ya que puede aumentar el nivel de complejidad de esta innecesariamente. Forma normal 1 (1NF): En esta forma se establece que se deben eliminar todos los grupos repetitivos en las tablas. Además se debe crear una tabla separada para los tipos de datos relacionados. Por último se establece que cada tabla debe tener una clave primaria para identificar a sus datos, esta clave primaria debe ser única para cada elemento de la tabla. Forma normal 2 (2NF): La segunda forma normal establece que las tablas que poseen grupos de datos que se aplican a varios elementos a la vez se deben crear tablas separadas para dichos datos y se debe relacionar dichas tablas mediante claves externas. Forma normal 3 (3NF): El tercer nivel de normalización tienen como objetivo evitar que existan datos duplicados o errores en estos, por lo cual se debe ubicar en tablas separadas los elementos que no dependan de la clave, es decir los elementos en la tabla que no sean exclusivos de el elemento de la tabla que se identifica con el identificador de la tabla. El tercer nivel de de normalización implica que las tablas pueden crecer todo lo que quieran 18 sin duplicación ni corrupción de datos. Este nivel de normalización se dice suficiente para la mayoría de las aplicaciones, ya que puede manejar los datos obtenidos en su totalidad de una manera fácil. Forma normal 4 (4NF): En casos específicos en los que se tiene una relación de “varios­con­varios” es decir, que un elemento de la tabla A tenga varios elementos de la tabla B, y que al mismo tiempo un elemento de la tabla B tenga varios elementos de la tabla A, se necesita un nivel de formalización 4. Este nivel de formalización indica que cuando existen relaciones de “varios­con­varios”, las entidades independientes no se pueden almacenar en la misma tabla. Forma normal 5 (5NF): Este nivel de normalización se aplica algunas veces, sin embargo en la mayoría de los casos no es necesario para obtener una mejor funcionalidad en nuestra aplicación. El principio de esta dice que se puede reconstruir la tabla original desde las tablas resultantes en las que fue separada dicha tabla. El aplicar esta regla permite asegurar que no se ha creado ninguna entrada extraña en las tablas, y que la estructura de tablas creada es del tamaño justo. Sin embargo no es necesaria a menos que se este trabajando con una estructura de datos demasiado extensa. 2.9 El lenguaje de programación Ruby [5] [7]. Ruby es un lenguaje de programación orientado a objetos, interpretado y multiplataforma, fue creado por Yukihiro Matsumoto en 1993 y presentado públicamente en 1995. La sintaxis del lenguaje esta inspirada en otros lenguajes como Perl y Python, además posee características de programación orientada a objetos. Además comparte funcionalidad con otros lenguajes de programación como Lisp, Lua, Dylan y CLU. Su implementación oficial es distribuida bajo una licencia de software libre. 19 El nombre del lenguaje procede de una broma en alusión al nombre del lenguaje Perl, ya que ambos hacen referencia a nombres de piedras preciosas. La última versión estable del lenguaje hasta este momento es la 1.8.6 que fue publicada en Diciembre del 2007, mientras que ese mismo mes apareció la versión 1.9.0 que es una versión de desarrollo que presenta varias mejoras. 2.9.1 Filosofía del lenguaje. El creador de este lenguaje ha expresado que su creación fue hecha pensando en la productividad así como la facilidad y diversión para el programador, es por eso que presenta una buena interfaz al usuario, dándole un mayor énfasis a las necesidades del programador que a las de la máquina. El lenguaje sigue un principio llamado “principio de la menor sorpresa”, que quiere decir que el lenguaje debe generar la menor confusión de los usuarios experimentados, esto debido a que el creador decidió hacer un lenguaje que fuera capaz de hacer la programación más divertida para el mismo, minimizando el trabajo a la hora de programar y evitando confusiones. 2.9.2 Semántica. En este lenguaje todos los tipos de datos son considerado como un objeto, incluyendo las clases y los tipos que en otros lenguajes son definidos como primitivas. Todas las funciones don métodos y las variables son referencias a objetos, pero nunca un objeto por si solas. Permite además utilizar programación procedural, es decir que se pueden definir funciones y y variables fuera de las clases, todas estas definiciones serán parte de un objeto raíz llamado Object. En Ruby no se requiere de polimorfismo de funciones ya que en todas las funciones se pueden pasar datos de distinta clase en cada llamada del método, debido a que el lenguaje maneja tipos de datos dinámicos. 20 El polimorfismo de tipos si es soportado, es decir que se pueden manejar clases utilizando la interfaz de su clase padre. 2.9.3 Sintaxis. La sintaxis de este lenguaje es muy parecida a la de Perl y Python. Las clases y los métodos se definen con palabras claves. Las variables pueden llevar prefijos para indicar el rango de valores que puede tomar esa variable. Las palabras claves se utilizan para definir valores sin utilizar llaves, a diferencia de otros lenguajes como Perl y C. Los saltos de línea son interpretados como el final de una sentencia, el punto y coma también se puede utilizar para dicho propósito. En Ruby las variables de clases privadas se mantienen dentro de ellas, y solo se pueden utilizar a travez de métodos de acceso. Se obliga a todas las variables de clases a ser privadas pero también se proporciona una forma sencilla de utilizar métodos de acceso a ellas. 2.9.4 Metaprogramación. La metaprogramación es una de las características de Ruby. Consiste en crear programas que puedan manipular o inclusive crear otros programas como datos, o realizar durante la compilación parte del trabajo que se realizaría durante la ejecución de otra forma. Esta característica permite al programador ahorrar tiempo de desarrollo de código. El compilador es la herramienta que comúnmente utiliza la metaprogramación, ya que permite al programador realizar un programa en un lenguaje de alto nivel y luego lo traduce a lenguaje ensamblador, lo cual ahorra mucho tiempo. 2.10 Modelo Vista Controlador [9] [10]. El Modelo Vista Controlador es un patrón de diseño de software que se utiliza en el diseño de aplicaciones con interfaces sofisticadas. 21 Este patrón permite separar una aplicación en tres componentes distintos: La lógica de control, los datos y la interfaz de usuario. Es utilizado comúnmente en aplicaciones web en donde la vista es el código HTML y otros códigos que permiten desplegar datos dinámicamente, el modelo es el sistema gestor de bases de datos y la lógica de negocio mientras que el controlador es el mecanismo que recibe los eventos desde la vista. 2.10.1 Descripción del patrón. El patrón esta conformado por tres elementos como se mencionó: Modelo: El modelo es el encargado de acceder a la capa de almacenamiento de datos, además de definir la funcionalidad del sistema. Además debe llevar un registro de las vistas y controladores del sistema, para que sea capaz de notificar a estos sobre los cambios que pueda producir un agente externo a los datos. Vista: La vista se encarga de recibir los datos de un modelo y mostrarlos al usuario. Debe tener además un registro del controlador que por lo general es instanciado por esta fase. Presenta el modelo en un formato que pueda interactuar con el usuario, por lo general es la interfaz del usuario. Controlador: Es el encargado de recibir los eventos de entrada e interpretarlos para determinar la acción que debe realizar cada evento. Por ejemplo cuando un usuario realiza un clic sobre un botón el controlador debe recibir la solicitud y determinar la acción a realizar. 2.11 Ruby on Rails [6] [11]. Ruby on Rails es un framework para diseño de aplicaciones web de código abierto escrito en el lenguaje Ruby. Este sigue el paradigma de la arquitectura de Modelo Vista Controlador (MVC). Un framework es una estructura de desarrollo de software a partir de la cual se pueden desarrollar otros proyectos. Por lo general incluye soporte de programas, bibliotecas y lenguaje interpretado que permite unir los componentes del proyecto. 22 Ruby on Rails fue escrito por David Heinemeier Hansson y liberado al público por primera vez en el año 2004. A partir de ese momento se han lanzado varias versiones, siendo la más actual la versión 2.1 publicada el 1 de Junio del 2008. La principal característica de este framework es que permite desarrollar aplicaciones de utilidad en el mundo real de una forma sencilla, reduciendo la cantidad de código y de configuración necesarios con respecto a otras opciones. La metaprogramación propia del lenguaje Ruby es utilizada por Ruby on Rails para permitir una sintaxis más legible para los usuarios. Existen muchos proyectos y páginas conocidos que utilizan Ruby on Rails, que aunque es muy reciente ha tenido gran aceptación entre los usuarios además que posee gran documentación en línea para facilitar la ayuda a desarrolladores. 2.11.1 Principios Fundamentales. Ruby on Rails utiliza dos principios fundamentales como lo son el principio de “No te repitas” y el de “Convención sobre Configuración”. El principio de “No te repitas” se refiere al hecho de realizar las definiciones una sola vez. Es por eso que a la hora de programar los componentes se integran de manera que no es necesario establecer puentes entre ellos ya que el mismo sistema los realiza, por ejemplo si se quiere definir una clase no es necesario especificar los nombres de las columnas sino que son identificadas automáticamente a través de la base de datos. El principio de “Convención sobre Configuración” busca que el programador únicamente deba especificar las configuraciones que no sean convencionales. Esto permite que al realizar una base de datos partiendo de cero, se puedan seguir las convenciones propias de Ruby on Rails lo que ahorra código. 2.11.2 El Modelo Vista Controlador de Ruby on Rails. 23 Ruby on Rails presenta un patrón Modelo Vista Controlador definido, los elementos de este modelo son los siguientes: Modelo: El modelo consiste básicamente en las clases que son la representación de las diferentes tablas de la base de datos. Para acceder a las clases del modelo se debe únicamente heredar la clase ActiveRecord::Base y el programa encontrará que tabla debe utilizar y que columnas posee dicha tabla. Al definir una clase se detalla a su vez las relaciones entre las clases mediante una estructura objeto relacional. Las rutinas para validación de datos así como las de actualización también son especificadas e implementadas en el modelo. Vista: La vista presenta la lógica que permite la visualización de los datos de las clases del Controlador. Esta es considerada por lo general para aplicaciones web como el código incluido en HTML. En Rails se utiliza un modelo de Ruby integrado, que utiliza archivos con formato RTML. Estos están compuestos por códigos HTML, mezclados con código de Ruby. También se pueden construir vistas en HTML o XML. Cada método del controlador debe llevar un código en HTML para mostrar información al usuario. La distribución de los elementos de la página se describe separadamente de las acciones del controlador. Controlador: Estas clases son las que responden a la interacción con el usuario y determinan la lógica que se debe seguir cuando se da un evento, para manipular los datos de las clases y mostrar los resultados a través de la vista. Estos métodos son invocados por el usuario utilizando un navegador web en aplicaciones de este tipo. El controlador es implementado en Ruby on Rails por el ActionPack. A partir de la clase 24 ApplicationController se pueden heredar otras clases para definir acciones como métodos para invocar a través del navegador web. Ruby on Rails permite construir de forma rápida la mayor parte de la lógica de control y las vistas necesarias para las operaciones más comunes. 2.11.3 Soporte de bases de datos. Ruby on Rails proporciona soporte para la utilización de bases de datos, favoreciendo su uso a través de un gestor de bases de datos. Se soporta la biblioteca SQLite en caso de que no se pueda utilizar una base de datos. La interacción con la base de datos es abstracta desde el punto de vista del programador, y los accesos son realizados automáticamente sin necesidad de utilizar consultas de tipo SQL aunque de querer usarse se pueden utilizar. Entre los sistemas gestores de base de datos soportados se encuentran: MySQL, PostgreSQL, SQLite, IBM DB2, Oracle y Microsoft SQL Server. 25 CAPÍTULO 3: Estructura general del sistema de inventario de bodega. 3.1 Estructura de la aplicación y aspectos generales. La base de datos del sistema desarrollado esta dividida en dos partes esenciales: Una base de datos que maneja los activos de la bodega, con sus diferentes marcas, modelos y estados de los activos, y otra tabla que maneja los usuarios en la cual se almacena la información personal de estos así como la información utilizada para la conexión de estos al sistema como lo son el nombre de usuario y las contraseñas. Para la base de datos se utilizó el gestor de base de datos PostgeSQL, que es de uso libre, además de ser uno de los más reconocidos por su excelente manejo de datos permitiendo conservar su integridad y coherencia. La aplicación se desarrollo en el lenguaje de programación Ruby utilizando el framework Ruby on Rails, el cual permite implementar el manejo de la base de datos de una forma más sencilla, ahorrando al programador mucho código que se puede hacer genérico para todas las aplicaciones. La aplicación utiliza una estructura modelo­vista­controlador, por lo tanto los códigos estas clasificados según estas tres secciones. Todos estos archivos relacionados directamente con la aplicación se encuentran en la carpeta “app”, divididos en carpetas separadas para cada una de las secciones. Además existen varios archivos de configuración así como librerías utilizadas entre otras cosas. 3.2 Base de datos. Como ya se mencionó se utiliza es gestor de base de datos PostgreSQL, para el desarrollo de la aplicación se utilizó la versión 8.3.3. 26 Se utiliza un adaptador que se encarga de realizar el manejo de la base de datos, incluyendo la creación de las bases de datos a utilizar y la creación de sus tablas, traduciendo código Ruby en sentencias SQL para realizar dichas tareas. La base de datos se encarga del almacenamiento de forma segura y confiable de la información, tanto de activos de bodega como de usuarios, sesiones y roles utilizados para la autenticación al sistema. 3.3 Modelo. El modelo es la parte encargada del manejo directo de la base de datos y sus relaciones. Es la representación de la base de datos desde la aplicación, de manera que maneja las tablas y sus elementos como clases, de manera que se pueda hacer referencia a ellas de una forma sencilla desde cualquier parte del código. En esta parte se definen además las sentencias de mapeo objeto­relacional que describen la relación entre las clases, es decir entre los elementos de las tablas de la base de datos. Además se definen las rutinas de validación de datos que permiten conservar la integridad de los mismos, especificando que reglas se deben seguir antes de introducir un valor a la base de datos. Se definen también algunas funciones que requieren la revisión o manipulación directamente de los valores de la base de datos, por ejemplo si se quiere encriptar la contraseña de los usuarios se puede definir aquí la función que realice dicha tarea. Entre las funciones que se puede definir esta también funciones para filtrar los datos ingresados mediante expresiones regulares que permitan eliminar caracteres indeseados en una entrada de un valor a la base de datos. 3.4 Controlador. El controlador es el encargado de manejar los eventos que se realizan en la aplicación, por lo 27 general provocados por una acción del usuario. Se tienen controladores separados para cada tabla de la base de datos, por defecto, aunque se pueden crear nuevos controladores para otras acciones. Las acciones básicas que maneja el controlador son las de nuevo, crear, editar, actualizar, destruir y listar y mostrar. Nuevo: Cuando se quiere crear un nuevo elemento esta acción se encarga de crear un elemento en blanco que luego es actualizado con los datos que se ingresan por el usuario. La necesidad de crear un elemento en blanco es que de esta forma se puede asignar en la vista un campo de la variable a cada elemento de la forma que debe llenar el usuario. Crear: La acción de crear se encarga de tomar los datos ingresados por el usuario y actualizar el elemento que previamente fue creado con la acción nuevo. Editar: Editar se encarga de asignar en la vista los valores actuales de el elemento a editar de forma que el usuario lo modifique a placer. Actualizar: Toma los elementos modificados por el usuario en la vista de editar, y los actualiza en la base de datos. Destruir: Destruye el o los elementos seleccionados, eliminándolo de la base de datos. Listar: Hace una búsqueda y muestra una lista de los elementos que el usuario desea ver. Mostrar: Muestra el contenido de un elemento específico que el usuario quiere ver. En algunos casos hay acciones que no se necesitan, como por ejemplo en el caso de las sesiones, estas se crean cuando un usuario se conecta al sistema, sin embargo no es necesario editarlas ni actualizarlas, simplemente se eliminan cuando el usuario se desconecta, por lo tanto dichas acciones no están definidas para este caso. 3.5 Vista. La vista maneja la interfaz del sistema con el usuario. Esta dividida también según el 28 elemento a desplegar y la acción que se va a realizar. Por ejemplo existe una vista para listar los activos, otra con la forma para que el usuario cree un nuevo activo con los datos requeridos, otra muy similar a la anterior con una forma que despliega los datos actuales de el activo para ser modificados por el usuario, así como una vista para mostrar los datos de un activo específico. Estas vistas se encargan de comunicarse con el controlador para tomar los datos que este le pide al modelo y desplegarlos en pantalla al usuario. Las vistas están en lenguaje HTML, sin embargo poseen código Ruby integrado mediante una sentencia especial que permite insertar estos códigos. Aparte de una vista para cada acción a realizar exista una archivo de máscara o layout que maneja una interfaz alrededor de la pantalla donde se despliegan los datos, es decir, maneja la barra de título de la aplicación, la barra lateral y en fin, la estructura general de la interfaz con el usuario. Ruby on Rails crea un layout por defecto para cada una de las tablas, sin embargo, en nuestra aplicación se utilizó un único layout para desplegar todas las vistas, el cual fue llamado “aplicación.html.rb”. El layout utilizado no posee ninguna descripción de estilos y formatos, para esto se utiliza un archivo nombrado “inventario.css” el cual esta escrito en un lenguaje de Hoja de estilo en cascada (CSS), en el cual se especifican los tipos y tamaños de letra, el color y las tabulaciones de los diferentes elementos de las tablas. 3.6 Archivos de configuración. Los archivos de la carpeta “config” poseen la configuración de la aplicación, donde se puede definir configuraciones para los diferentes ambientes de trabajo, así como la configuración de la base de datos, las rutas por defecto para las vistas e información relacionada con los plugins instalados para la aplicación. Los archivos de configuración más importantes son: database.yml: En este archivo se especifica las características de la base de datos a utilizar. 29 El nombre de la base de datos, el adaptador para manejar dicha base de datos, la codificación, el usuario de la base de datos y la contraseña son los elementos descritos en este archivo, para cada uno de los ambientes de trabajo, ya sea desarrollo, producción o prueba. environment.rb: En este archivo posee información relacionada con las “gemas” instaladas y que la aplicación va a utilizar. Las gemas son bibliotecas de Ruby que se administran de forma sencilla con el comando “gem” desde la terminal. routes.rb: En este archivo se especifica las rutas por las cuales se va a invocar las diferentes vistas en el navegador. Además se especifica la ruta por defecto de la aplicación. 3.7 Otras carpetas. El sistema esta compuesto por otras carpetas que contienen también información importante para el funcionamiento de la aplicación. A continuación se resume la función y contenido de las más relevantes: db: Tiene información de la base de datos. En ella se guarda el código de las diferentes migraciones realizadas para la base de datos. Las migraciones permiten modificar las bases de datos sin comprometer la integridad de los datos existentes. Además en el archivo “schema.rb” se presenta la descripción de la estructura actual de la base de datos, así como la versión de la migración que se esta corriendo. lib: Contiene información de bibliotecas instaladas el el proyecto. En la aplicación realizada contiene archivos de uno de los plugins instalados, necesarios para su buen funcionamiento. log: Contiene un registro de las notificaciones desplegadas por el sistema durante todo el desarrollo de la aplicación. Así como algunas notificaciones de algunos de los plugins utilizados que permiten revisar las acciones que se han realizado. public: Posee archivos web que no cambian como las imágenes, las hojas de estilo y los archivos en lenguaje JavaScript, además de algunos archivo en HTML. 30 script: Esta carpeta contiene scripts para poder correr varias herramientas que se utilizan con Ruby on Rails. Entre los scripts que se pueden encontrar están los utilizados para generar código y el que se utiliza para arrancar el funcionamiento del servidor de desarrollo. vendor: Este directorio contiene bibliotecas desarrolladas por terceros, es decir que no corresponden a la distribución de Ruby on Rails. Por ejemplo aquí se encuentran las bibliotecas utilizadas por los plugins instalados dentro de la carpeta del proyecto. Index: Esta carpeta es creada por el plugin “Act­as­ferret”. En ella se guardan los índices generados por las búsquedas que se hayan realizado de manera que las búsquedas subsiguientes puedan utilizar esta información para agilizar el tiempo de respuesta de dicha búsqueda. 3.8 Plugins utilizados. Se utilizaron 6 plugins para el desarrollo de la aplicación. Tres de ellos se utilizaron para poder crear llaves foráneas para realizar las relaciones entre las tablas y aumentar la confiabilidad del sistema en el manejo de los datos. Muchas referencias indicaban que en Ruby on Rails no era necesario crear estas llaves foráneas, ya que la aplicación se encarga de manejar la relación entre los datos. Sin embargo, la creación de estas llaves permite un manejo más seguro y confiable de estos datos ya provoca que el propio sistema gestor de base de datos compruebe estas relaciones. Los tres plugins utilizados para este propósito fueron: redhillonrails_core: Este plugin contiene bibliotecas necesarias para el funcionamiento de los otros dos plugins. foreign_key_migrations: Crea llaves foráneas en la base de datos automáticamente a la hora de realizar una migración de la base de datos. Se pueden especificar de varias formas las llaves foráneas deseadas en el sistema. Al crear una columna de la forma “cosa_id”, este automáticamente crea una llave foránea usando el identificador de la tabla “cosas” foreign_key_associations: Crea automáticamente relaciones en el modelo basado en las llaves 31 foráneas existentes en la base de datos. Los otros tres plugins utilizados en fueron: Restful_authentication: Crea una estructura base para la creación de un sistema de autenticación de usuarios. acts_as_ferret: Realiza búsquedas en la base de datos del sistema. Permite utilizar varias funciones para búsquedas con diferentes parámetros. will_paginate: Utilizado para la paginación de los elementos desplegados en pantalla. Se consideró que al crecer la base de datos es mejor desplegarla de una manera más ordenada como lo es mediante páginas, especificando un número razonable de elementos por página de manera que si se desea cargar una tabla con muchos elementos la página cargue estos por secciones aumentando la velocidad de navegación de la página web. 32 CAPÍTULO 4: Diseño de la base de datos. Como se explicó anteriormente, la base de datos esta dividida en dos secciones principales. El primer conjunto de tablas se encarga de almacenar la información objetivo del sistema, es decir la información de los activos de inventario junto con sus marcas, modelos y estados. La otra parte de la base de datos es la encargada de almacenar la información de los usuarios creados en el sistema que es utilizada por el sistema de autenticación. Aquí se tiene una tabla para información de los usuarios, otra en la que se almacenan los roles existentes y una que contiene la información que describe cuales roles posee cada usuario. 4.1 Base de datos para activos de bodega. Para diseñar esta parte de la base de datos se investigó con el encargado de la bodega de la Escuela de Ingeniería Eléctrica de la Universidad de Costa Rica, cual era la información importante que debía poseer cada uno de los activos de bodega y cuales clasificaciones era importante realizar con estos. La información que se debe almacenar en la base de datos es la siguiente: Cuadro 1. Activos en nivel de normalización 0. Activos #Placa #Serie Modelo Descripción Marca Estado Aplicando la primera regla de normalización de bases de datos se agrega un identificador a la tabla. Se consideró la opción de utilizar el número de placa como identificador, sin embargo la posibilidad de que este número de placa no exista o este repetido evita que se pueda utilizar para esto. Una vez incluida la columna del identificador se tiene: Cuadro 2. Activos en nivel de normalización 1. Activos Activo Id #Placa #Serie Modelo 33 Descripción Marca Estado El segundo nivel de normalización indica que se deben de crear tablas separadas para los grupos de datos que apliquen a varios registros. En nuestro no existe ningún grupo de datos que este aplicado varias veces a un registro por lo tanto no es necesario realizar ningún cambio. El tercer nivel de normalización indica que se deben de eliminar los campos que no dependen de la clave. Podemos notar como el modelo con su respectiva descripción es independiente de cada activo individual, por lo cual se puede separar en una nueva tabla. De igual forma el estado del equipo es independiente de la clave primaria de el activo de manera que la estructura de tablas se convierte en la siguiente: Cuadro 3. Activos en nivel de normalización 2. Activos Activo Id #Placa #Serie Rel. Modelo_id Rel. Estado_id Cuadro 4. Estados en nivel de normalización 2. Estados Estado Id Nombre_estado Cuadro 5. Marcas en nivel de normalización 2. Modelos Modelo_id Nombre_Modelo Descripción Marca Pero podemos notar como en esta última tabla la marca puede aplicarse también a varios modelos por lo que esta no depende de la llave primaria de dicha tabla por lo que se crea una tabla separada para las marcas y se relacionan las tablas mediante una llave foránea, de la siguiente forma: Cuadro 6. Modelos en nivel de normalización 3. 34 Modelos Modelo_id Nombre_Modelo Descripción Rel. Marca_id Cuadro 7. Marcas en nivel de normalización 3. Marcas Marca Id Nombre_marca Figura 2. Diagrama E­R sistema de manejo de activos. Tenemos ahora cuatro tablas separadas. El cuarto nivel de normalización se dice que se debe aplicar en situaciones en las que se da una relación de varios con varios. En nuestro caso no es necesario este nivel por lo tanto se considera que el tercer nivel es suficiente para que se de un manejo fácil y seguro de los datos. 4.2 Base de datos para manejo de usuarios. La segunda parte de la base de datos es la que esta relacionada con el manejo de usuarios del sistema. En esta parte se utilizó un esquema siguiendo algunos lineamientos brindados por el plugin 35 instalado para dicho propósito. Además se decidió añadir algunos campos para información personal de los usuarios como lo son el nombre, el teléfono, número de cédula y carné en caso de existir. El esquema posee 3 tablas: Una tabla de usuarios con la información de estos, una de roles, con los datos de los diferentes roles creados y una tabla de permisos en la cual se relacionan los usuarios con diferentes roles para de esta forma asignar los permisos a los usuarios según el nivel de acceso a la aplicación deseado para cada uno de ellos. La base de datos de usuarios contiene las siguientes columnas: ● Id_usuario: El identificador de la tabla. ● Login: El nombre del usuario en el sistema. ● Email: El correo electrónico. ● crypted_password: La contraseña encriptada. ● Salt: Variable utilizada para la encriptación de los datos. ● remenber_token: Un identificador para ser recordado por el sistema en un computador específico. ● remember_token_expires_at: El tiempo de vencimiento del identificador. ● Nombre: El nombre de la persona asignada a dicho usuario. ● Apellido: El apellido de la persona asignada a dicho usuario. ● Teléfono: El número telefónico del usuario. ● Cédula: La cédula de identidad de la persona. ● Carné: En caso de ser estudiante se almacena la información de su carné. La tabla de roles almacena el nombre de los roles existentes en el sistema, de manera que se le pueda asignar dicho rol a un usuario para brindarle permisos especiales a este. La tabla posee la 36 siguiente información: Cuadro 8. Tabla de roles. Roles Role_Id Rolename La tabla de permisos almacena la información de cuales usuarios están relacionados con ciertos roles, es decir, los permisos que cada usuario posee en el sistema: Cuadro 9. Tabla de permisos. Permisos Permissions_Id role_Id User_Id El archivo “schema.rb” de la carpeta “db” nos muestra finalmente la estructura de la base de datos con sus llaves foráneas respectivas. ActiveRecord::Schema.define(:version => 20081126080143) do create_table "activos", :force => true do |t| t.string "num_serie" t.integer "placa" t.integer "modelo_id" t.integer "estado_id" t.datetime "created_at" t.datetime "updated_at" end create_table "estados", :force => true do |t| t.string "nombre" t.datetime "created_at" t.datetime "updated_at" end create_table "marcas", :force => true do |t| t.string "nombre_marca" t.datetime "created_at" t.datetime "updated_at" 37 end create_table "modelos", :force => true do |t| t.string "nombre_modelo" t.string "descripcion" t.integer "marca_id" t.datetime "created_at" t.datetime "updated_at" end create_table "permissions", :force => true do |t| t.integer "role_id", :null => false t.integer "user_id", :null => false t.datetime "created_at" t.datetime "updated_at" end create_table "roles", :force => true do |t| t.string "rolename" t.datetime "created_at" t.datetime "updated_at" end create_table "users", :force => true do |t| t.string "login" t.string "email" t.string "crypted_password", :limit => 40 t.string "salt", :limit => 40 t.datetime "created_at" t.datetime "updated_at" t.string "remember_token" t.datetime "remember_token_expires_at" t.string "nombre" t.string "apellido" t.integer "telefono" t.string "carne" t.integer "cedula" end add_foreign_key "activos", ["modelo_id"], "modelos", ["id"], :name => "activos_modelo_id_fkey" add_foreign_key "activos", ["estado_id"], "estados", ["id"], :name => "activos_estado_id_fkey" add_foreign_key "modelos", ["marca_id"], "marcas", ["id"], :name => "modelos_marca_id_fkey" add_foreign_key "permissions", ["role_id"], "roles", ["id"], :name => "permissions_role_id_fkey" add_foreign_key "permissions", ["user_id"], "users", ["id"], :name => "permissions_user_id_fkey" end Entre las cosas que se puede notar es que Ruby on Rails al crear una tabla, crea automáticamente en ella dos columnas correspondientes a la fecha de creación y a la fecha de actualización del elemento, este valor se almacena automáticamente a la hora de crear y de editar un 38 elemento respectivamente. Esto permite llevar un seguimiento por fecha de las modificaciones en la base de datos y puede ser muy útil en algunos casos. Para llegar a la estructura anterior de la base de datos se debieron realizar varias migraciones de esta, ya que conforme avanzó el desarrollo de la aplicación, se encontraron nuevos requerimientos y se necesito realizar algunos cambios. De igual manera de necesitarse en el futuro agregar nuevos elementos o cambiar la estructura de la base de datos, se puede realizar de manera sencilla mediante migraciones. 39 CAPÍTULO 5: Modelo. El modelo es la sección del programa encargada de hacer el manejo directo de la base de datos. En el se definen los elementos de una tabla como objetos de Ruby y además se hace la validación de los datos introducidos, para definir las reglas en el manejo de los datos que contribuyan a cuidar la integridad de estos. Existe un modelo para cada una de las tablas presentes en la base de datos. 5.1 Modelos del sistema de activos. activos.rb: class Activo < ActiveRecord::Base belongs_to :estado belongs_to :modelo acts_as_ferret :fields => [:num_serie, :placa] def self.per_page 40 end validates_uniqueness_of :placa validates_numericality_of :placa, :allow_nil => true validates_presence_of :num_serie, :placa end Podemos ver como se definen las relaciones en entre las tablas. En este caso se especifica que cada modelo pertenece a un estado y a un modelo. La sentencia “acts_as_ferret” es utilizada por el plugin del mismo nombre. Mediante esta se especifica que se va a utilizar el sistema de búsquedas en las columnas especificadas luego entre paréntesis. Se consideró que las búsquedas de los activos se debían realizar por su número de serie o por su placa. De esta manera se puede encontrar un activo específico cuando se necesite. La sentencia “def_self.per_page” define el número de elementos que se debe desplegar por página. Este número es el parámetro que utiliza el plugin de paginación para saber como acomodar los elementos por página y es muy sencillo cambiarlo para ajustarlo al gusto del usuario. 40 Se quiere que el valor de la placa sea un valor único, y además que sea siempre un número, por lo que se indica que se debe validar este dato para comprobar que sea un número. Finalmente se especifica que tanto el número de serie como la placa deben estar presentes, de otra forma no se permite crear o editar el activo. estados.rb: class Estado < ActiveRecord::Base has_many :activos end En este modelo se define únicamente la relación entre los estados y los activos. Se especifica entonces que cada estado tendrá muchos activos asignados. modelo.rb: class Modelo < ActiveRecord::Base belongs_to :marca has_many :activos acts_as_ferret :fields => [:descripcion, :nombre_modelo] end En este caso se especificó que los modelos pertenecen a una marca. Además cada modelo posee varios activos. Para este caso se aplicó también el sistema de búsquedas y se especifico que estas se pueden realizar en la columna de nombre del modelo así como en su descripción. modelo.rb: class Marca < ActiveRecord::Base has_many :activos, :through => :modelos has_many :modelos acts_as_ferret :fields => [:nombre_marca] end En el caso de las marcas, se define que estas tienen muchos modelos, además como cada modelo posee varios activos, se especifica que cada marca tiene muchos activos a través de los modelos. 41 De nuevo se utiliza el plugin para búsquedas, en este caso se realizan búsquedas en la columna de nombre de la marca. 5.2 Modelos del sistema de autenticación de usuarios. Como se menciono anteriormente tenemos una tabla de permisos usuarios que almacena la información de todos los usuarios registrados en el sistema. Además existe una tabla de roles que posee una identificación de los roles existentes y una tabla de permisos que posee la información de cuales usuarios poseen cuales permisos. A cada usuario se le puede asignar varios permisos, es decir varios roles. user.rb: attr_accessor :password acts_as_ferret :fields => [:login, :email, :nombre, :apellido, :telefono, :carne, :cedula] validates_presence_of :login, :email validates_presence_of :password, :if => :password_required? validates_presence_of :password_confirmation, :if => :password_required? validates_length_of :password, :within => 4..40, :if => :password_required? validates_confirmation_of :password, :if => :password_required? validates_length_of :login, :within => 3..40 validates_length_of :email, :within => 3..100 validates_uniqueness_of :login, :email, :case_sensitive => false validates_numericality_of :telefono validates_numericality_of :cedula has_many :permissions has_many :roles, :through => :permissions before_save :encrypt_password Las anteriores sentencias definen la validación de datos para los usuarios. En este caso tenemos que se activan las búsquedas en el nombre del usuario, en el correo electrónico, en el nombre y apellido de la persona, el teléfono, carné y cédula. Además se define que tanto el nombre de usuario, como el correo, la contraseña y su confirmación deben estar presentes a la hora de crear un usuario, de otra forma se presenta un error. El nombre de usuario y el correo electrónico deben tener un largo determinado para ser válidos, por lo que se especifica un largo mínimo de 3 caracteres en ambos casos y un máximo de 40 y de 100 para el nombre de usuario y el correo respectivamente. Estos deben ser únicos además. 42 La validación de datos especifica además que los campos de nombre de usuario y correo deben ser únicos. Los campos de teléfono y número de cédula deben ser solo números, de esta manera se evita que se ingresen caracteres no deseados a la tabla que pueden alterar el dato que se quiere. En cuanto a las relaciones con otras tablas, se define que los usuarios tienen muchos permisos, y a su vez tienen muchos roles a través de los permisos. En este archivo además se especifican varias funciones que tienen que ver con el chequeo de la base de datos. Entre estas funciones se encuentran las que se encargan de encriptar los datos de las contraseñas, las que se encargan de recordar un usuario en un equipo y otras funciones a las cuales no se les dio un uso en nuestra aplicación ya que algunas funcionalidades como la activación por correo no eran necesarias según los requerimientos de la aplicación. role.rb: class Role < ActiveRecord::Base has_many :permissions has_many :users, :through => :permissions end El modelo de los roles define únicamente que estos poseen varios permisos y que además a través de estos permisos posee muchos usuarios. permissions.rb: class Permission < ActiveRecord::Base belongs_to :user belongs_to :role end En el caso de los permisos se puede notar que el modelo especifica que estos pertenecen a usuarios y a roles al mismo tiempo. De esta manera se maneja en esta tabla la relación entre los usuarios y los roles, quedando finalmente una relación de muchos contra muchos, ya que un usuario 43 puede tener varios roles y a su vez un rol puede estar asociado a varios usuarios al mismo tiempo. 44 CAPÍTULO 6: Controladores. Los controladores son las partes del código encargadas de realizar las acciones que el usuario ordene. Estas pueden ir desde desplegar elementos en pantalla, hasta hacer modificaciones a la base de datos agregando, quitando o modificando elementos en esta. Las acciones básicas que Ruby on Rails define por defecto son: Nuevo, crear, editar, actualizar, destruir, mostrar y listar. Se sabe que Ruby on Rails crea estas acciones por defecto al crear la estructura para cada una de sus tablas. En varios controladores de la aplicación estas acciones no fueron modificadas, por lo tanto son idénticas en varios controladores. Por lo tanto se va a comentar estas una única vez y además se van a explicar los cambios realizados en el caso de haberlos. 6.1 Controladores de sistema de activos. Se tienen como se mencionó muchos códigos que son similares en los controladores de el sistema de activos. Debido a esto se menciona primero los códigos que son similares en todos los casos, con el objetivo de evitar la explicación repetida de códigos. Las siguientes líneas están presentes en todos los controladores de el sistema de activos como lo son: Activos, modelos, marcas y estados. include AuthenticatedSystem layout 'application' before_filter :login_required, :only => [:show, :index] before_filter :check_administrator_role, :only => [:destroy,:new, :edit, :update] Al inicio del archivo tenemos las sentencias anteriores. En ellas se define que se va a incluir las librerías del sistema de autenticación de usuarios, esto para utilizar seguidamente algunas funciones para aplicar los filtros a las acciones del controlador. Se define que se va a utilizar el layout “application”, el cual fue creado como una máscara genérica para todos los controladores. Esto se explicará más adelante en el capítulo de vistas. Se definen además los filtros a la página. Mediante esto se puede generar permisos de 45 ingreso a las diferentes acciones del controlador. En este caso y en el de los otros controladores del sistema de activos, si un usuario se encuentra conectado al sistema. En caso de que este se dé, el usuario tiene acceso a las funciones de mostrar y de listar. Sin embargo si además de estar conectado, el usuario posee un permiso de administrador, se le brinda acceso a las funciones que realizan modificaciones de la base de datos, como lo son las de crear, editar y destruir elementos. def index @estados = Estado.find(:all) respond_to do |format| format.html # index.html.erb format.xml { render :xml => @estados } end end El método de listar mostrado anteriormente permite encontrar todos los elementos presentes en la tabla, mediante la función “find” que tiene como parámetro “:all” lo cual indica que se deben buscar todos los elementos presentes. Las siguientes líneas son funciones que utiliza Ruby para determinar si se debe desplegar la página en código HTML o en XML. Esto depende de las extensiones de los archivos creados en las vistas. Mediante esto Ruby genera automáticamente el código para desplegar lo que se desee, en el formato que se decida. def show @estado = Estado.find(params[:id]) respond_to do |format| format.html # show.html.erb format.xml { render :xml => @estado } end end El método de mostrar es muy similar al anterior, con la diferencia que en este caso se selecciona únicamente uno de los elementos de la tabla, especificando en la función “find” el identificador de este elemento, mediante “:id”. def new 46 @estado = Estado.new respond_to do |format| format.html # new.html.erb format.xml { render :xml => @estado } end end El método de “nuevo” permite crear un elemento vacío para desplegar una forma donde el usuario pueda asignar los valores deseados al elemento. Esto permite asignar una variable del elemento a cada uno de los campos de la forma para luego pasarle dichos parámetros introducidos por el usuario al método de “crear” para que este cree el elemento con estos parámetros. def create @estado = Estado.new(params[:estado]) respond_to do |format| if @estado.save flash[:notice] = 'El estado fue creado.' format.html { redirect_to(@estado) } format.xml { render :xml => @estado, :status => :created, :location => @estado } else format.html { render :action => "new" } format.xml { render :xml => @estado.errors, :status => :unprocessable_entity } end end end Como se mencionó anteriormente este método permite crear un nuevo elemento (en este caso un estado) con los parámetros introducidos por el usuario, que se indican a través de la sentencia “params[:estado]”. Además si el usuario es creado se especifica que se debe mostrar el elemento creado en la siguiente página, y desplegar un mensaje indicando que el elemento fue creado. En caso de que no se haya podido crear el elemento se debe volver a la página “nuevo” para modificar los parámetros. def edit @estado = Estado.find(params[:id]) 47 end El método de editar simplemente busca un elemento con el número de identificación elegido para agregar estos valores a la forma de manera que el usuario pueda modificar estos valores para realizar los cambios que quiera. def update @estado = Estado.find(params[:id]) respond_to do |format| if @estado.update_attributes(params[:estado]) flash[:notice] = 'El estado fue actualizado.' format.html { redirect_to(@estado) } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @estado.errors, :status => :unprocessable_entity } end end end El método de actualizar busca inicialmente el elemento que se va a modificar mediante el identificador indicado para luego hacer un llamado a la función “update_attributes” que actualiza los atributos de el elemento con los parámetros indicados entre paréntesis que provienen del formulario de edición que se modificó anteriormente. Se direcciona luego a mostrar el elemento modificado en caso de que dicha modificación se haya realizado satisfactoriamente, además se despliega un mensaje indicando que dicho elemento fue actualizado. En caso de que no se pueda realizar la actualización se vuelve a la página de editar para realizar los cambios correspondientes. def destroy @estado = Estado.find(params[:id]) @estado.destroy respond_to do |format| format.html { redirect_to(estados_url) } format.xml { head :ok } end end 48 El método de destruir busca el elemento que se desea eliminar a través de su identificador y llama al método “destroy” que se encarga de eliminarlo de la base de datos. Posteriormente envía a la página índice de los elementos. En el caso de los activos el método de listar fue modificado de manera que si se indica un estado o un modelo específico se despliegue solamente los activos que poseen ese atributo. Para esto fue necesario pasar como parámetro a este método ya sea el identificador de el modelo o de el estado según corresponda. En caso de que ninguno de los dos exista se despliegan todos los activos existentes. def index if params[:q] query = params[:q] @activos = Activo.find_by_contents(query, :limit => :all) else if (params[:estado_id].nil? and params[:modelo_id].nil?) @activos = Activo.paginate :page => params[:page], :order => 'updated_at DESC' else if params[:modelo_id].nil? @activos = Activo.paginate :page => params[:page], :conditions=> ["estado_id = ?", params[:estado_id]] else @activos = Activo.paginate :page => params[:page], :conditions=> ["modelo_id = ?", params[:modelo_id]] end end end respond_to do |format| format.html # index.html.erb format.xml { render :xml => @activos } end end Podemos notar como se verifica primero si existe algún parámetro de búsqueda, en caso de que exista se llama a la funcion “find_by_contents” que proporciona el plugin “acts_as_ferret”, mediante la cual se realiza la búsqueda de activos relacionados a el parámetro indicado. Si no existe el parámetro anterior se revisa la existencia de algún otro parámetro, para determinar cuales activos se deben desplegar. Si existe algún parámetro se revisa si es un identificador de modelo o de estado y en cada caso se se busca los elementos con este identificador. 49 En el caso del controlador de los modelos se utiliza la misma lógica para determinar si existe un parámetro de marca que haya sido enviado para desplegar únicamente los modelos pertenecientes a dicha marca en caso de existir el parámetro. Además se verifica la existencia de un parámetro de búsqueda también. if params[:q] query = params[:q] @modelos = Modelo.find_by_contents(query, :limit => :all) else if params[:marca_id].nil? @modelos = Modelo.find(:all) else @modelos = Modelo.find(:all, :conditions=> ["marca_id = ?", params[:marca_id]]) end 6.2 Controladores de sistema de autenticación de usuarios. Entre los controladores del sistema de usuarios tenemos un controlador que maneja todo lo correspondiente al manejo de los usuarios, es decir, la creación, edición, eliminación y despliegue de estos. Además de esto se tiene un controlador para realizar la actualización de las contraseñas, llamado “accounts”. Otro controlador es el que se encarga del manejo de los roles. Este permite crear roles así como asignarlos a usuarios. El último controlador es el que se encarga de crear y destruir las sesiones, este permite que una sesión sea recordada en un equipo específico. users_controller.rb: layout 'application' before_filter :login_required, :only => [:show, :edit, :update] before_filter :check_administrator_role, :only => [:index, :destroy, :new, :create] Al igual que en los otros controladores discutidos, se especifica que se va a utilizar el layout “application”. Seguidamente se definen los filtros para los usuarios. En este caso a un usuario normal se le permite únicamente las acciones de “mostrar”, “editar” y “actualizar”, eso si estas las 50 podrá realizar solo sobre su propio usuario. Al usuario administrador se le permite además crear, eliminar y ver usuarios. El método de listar es sencillo y únicamente se le agregó la verificación de parámetros para búsquedas. def index if params[:q] query = params[:q] @users = User.find_by_contents(query, :limit => :all) else @users = User.find(:all) end end Los métodos de “mostrar”, “nuevo”, “editar”, “actualizar” y “destruir” son idénticos a los explicados para el sistema de activos, por lo que se evitará su discusión. sessions_controller.rb: En este controlador se maneja la creación de sesiones de usuarios, es decir cuando un usuario conecta al sistema se crea una sesión, la cual es destruida en el momento en que este usuario se desconecta. layout 'application' before_filter :login_required, :only => :destroy before_filter :not_logged_in_required, :only => [:new, :create] Primero se define que se utilizará el layout “application” al igual que para todos los controladores de la aplicación. Además se aplican los filtros a los usuarios, de manera que si un usuario se encuentra conectado, el sistema le permita destruir la sesión, es decir desconectarse. De igual forma si un usuario no esta conectado se le permite crear una sesión, es decir conectarse al sistema. def create password_authentication(params[:login], params[:password]) end El método de “crear” llama el método “password_authentication” con los parámetros de el 51 nombre de usuario y la contraseña que hayan sido ingresados por el usuario. def password_authentication(login, password) user = User.authenticate(login, password) if user == nil failed_login("Su nombre de usuario o contraseña es incorrecto.") else self.current_user = user successful_login end end El método “password_authentication” a su vez llama el método “authenticate” del modelo de los usuarios, que verifica la existencia de un usuario con los parámetros introducidos. En caso de existir se iguala el usuario actual “current_user” a dicho usuario y se llama al método “succesful_login”. En caso de no existir se llama al método “failed_login” introduciendo como parámetro un mensaje de error indicando que el nombre de usuario o la contraseña no son correctos. def failed_login(message) flash.now[:error] = message render :action => 'new' end El método “failed_login” despliega el mensaje que se pasó como parámetro y envía de nuevo a la página de ingreso al sistema. def successful_login if params[:remember_me] == "1" self.current_user.remember_me cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at } end flash[:notice] = "Se ha conectado!" return_to = session[:return_to] if return_to.nil? redirect_to user_path(self.current_user) else redirect_to return_to end El método “successful_login” se encarga de revisar si el usuario marcó la casilla de “remember_me” para ser recordado en ese equipo, si la casilla fue marcada entonces se llama el método “remember_token” y “remember_token_expires_at” que se encargan de asignar una fecha en la que se debe dejar de recordar al usuario en ese equipo. El tiempo configurado es de 2 semanas, 52 sin embargo este parámetro es posible cambiarlo en el modelo “users.rb”. Además se despliega un mensaje indicando que se ha conectado el usuario al sistema y envía a la página de inicio del sistema, que se ha configurado como la lista de activos. def destroy self.current_user.forget_me if logged_in? cookies.delete :auth_token reset_session flash[:notice] = "Se ha desconectado." redirect_back_or_default('/') end El método de destruir llama a “forget_me” para evitar que el usuario siga siendo recordado por el sistema en ese equipo. Además borra de la cache del sistema el identificador que permite recordar al usuario. Luego despliega un mensaje indicando que el usuario ha sido desconectado del sistema y redirecciona a la ruta raíz de la aplicación. roles_controller.rb: Este controlador se encarga de realizar la gestión de roles para los usuarios. Sus funciones son las de crear y eliminar roles, desplegar los roles pertenecientes a un usuario y los roles que no han sido asignados a este, de manera que el administrador pueda asignar estos roles a los usuarios. layout 'application' before_filter :check_administrator_role Inicialmente se define que los métodos de este controlador serán accesibles únicamente a el o los usuarios administradores. def index @user = User.find(params[:user_id]) @all_roles = Role.find(:all) end El método de “listar” busca el usuario con el identificador especificado. Además busca todos los roles existentes para luego mostrar la relación entre el usuario y los roles existentes en la vista. Los métodos “mostrar”, “nuevo”, “crear” y “editar son idénticos a los descritos anteriormente para el sistema de manejo de activos. 53 El método actualizar, permite asignar un rol a un usuario: def update @user = User.find(params[:user_id]) @role = Role.find(params[:id]) unless @user.has_role?(@role.rolename) @user.roles << @role end redirect_to :action => 'index' end El método anterior busca tanto el rol como el usuario y si este usuario no posee el rol se procede a asignarlo y posteriormente redirecciona a listar de nuevo los roles para el usuario. El método destruir realiza la operación inversa del método actualizar, ya que permite quitar un rol a un usuario: def destroy @user = User.find(params[:user_id]) @role = Role.find(params[:id]) if @user.has_role?(@role.rolename) @user.roles.delete(@role) end redirect_to :action => 'index' end end Al igual que el método actualizar, este método busca el rol y el usuario y luego evalúa si el usuario posee el rol para de ser así borrarlo de la lista de roles del usuario que se almacena en la tabla de permisos. accounts_controller.rb: Este controlador se encarga únicamente de realizar la actualización de contraseñas de los usuarios, ya que este proceso se realiza por aparte a la actualización del resto de los datos del usuario. Debido a esto, el único método que posee es el de actualizar que se muestra a continuación: def update return unless request.post? if User.authenticate(current_user.login, params[:old_password]) if ((params[:password] == params[:password_confirmation]) && !params[:password_confirmation].blank?) current_user.password_confirmation = params[:password_confirmation] current_user.password = params[:password] if current_user.save flash[:notice] = "Contraseña actualizada." redirect_to root_path #profile_url(current_user.login) 54 else flash[:error] = "Ocurrió un error, su contraseña no fue cambiada." render :action => 'edit' end else flash[:error] = "La nueva contraseña no coincide con la confirmación." @old_password = params[:old_password] render :action => 'edit' end else flash[:error] = "Su contraseña vieja es incorrecta." render :action => 'edit' end end El método primero verifica que la contraseña vieja ingresada sea correcta. Luego revisa que la contraseña nueva sea igual a la confirmación de esta y que no sea una contraseña en blanco, para posteriormente salvar estas contraseñas para el usuario actual y desplegar una notificación indicando que la contraseña fue actualizada. Si no se puede guardar la contraseña se envía un mensaje de error indicando que no se pudo actualizar esta y se regresa a la página de edición de la contraseña. Luego, si la contraseña nueva asignada no coincidió con la confirmación de esta, o la contraseña vieja ingresada no coincide con la almacenada para ese usuario, se despliega un mensaje de error indicando cada caso. 55 CAPÍTULO 7: Vistas. Las vistas corresponden a la parte de la aplicación encargada de realizar la interfaz gráfica con el usuario. Al ser una aplicación web estas se realizan en códigos HTML, usando además llaves especiales para añadir código en lenguaje Ruby embebido dentro del HTML. Existen vistas para todos los controladores. Por ejemplo para activos existe una vista que lista los archivos y es llamada por el método “listar” del controlador. De igual forma existen vistas para los métodos “nuevo”, “mostrar” y “editar”. En algunos casos no fue necesario todas estas vistas debido a que algunas de estas acciones no estaban definidas en el controlador como se vio en el capítulo anterior. 7.1 Vistas del sistema de manejo de activos. En este caso las vistas de activos son las más representativas pues estas tuvieron la mayor cantidad de modificaciones. Debido a esto se explicará básicamente estas vistas aclarando que las vistas para los modelos, marcas y estados son muy similares e inclusive más sencillas por lo que sería repetitivo explicarlas. Como es de suponerse la vista creada por defecto para “nuevo” y para “editar” son casi idénticas, únicamente difieren en un par de textos. Debido a esto, para evitar realizar los cambios en ambas vistas se utiliza un código genérico que es utilizado por las dos vistas. Este código se llama parcial (partial) y se define iniciando el nombre del archivo con el símbolo “_”. _form.html.erb: #_form.html.erb <p> <%= f.label :num_serie %><br /> <%= f.text_field :num_serie %> </p> <p> <%= f.label :placa %><br /> <%= f.text_field :placa %> </p> <p> <%= f.label :modelo_id %><br /> <%= select("activo", "modelo_id", Modelo.find(:all).collect {|c| [c.nombre_modelo, c.id] }) %> 56 <%= link_to 'Crear modelo', new_modelo_path, :popup => true %></p> </p> <p> <%= f.label :estado_id %><br /> <%= select("activo", "estado_id", Estado.find(:all).collect {|c| [c.nombre, c.id] }) %> </p> <p> <%= f.submit label_text %> </p> <p><label Este código se encarga de desplegar el formulario para creación y edición de los activos. Primero despliega un campos de texto para ingresar el número de serie y de placa. Luego crea un lista desplegable con la lista de los modelos existentes y de los estados respectivamente. Finalmente se crean un botón para enviar los datos. Cabe destacar que los códigos que se presentan entre las llaves “<%= %>”, corresponden a códigos en lenguaje Ruby. En los archivos “new.html.erb” y “edit.html.erb” se especifica que se utiliza el anterior código y se pasa como variable el nombre del botón, ya que esta es la única diferencia entre las páginas de “crear” y de “editar”. new.html.erb: <h1>Nuevo activo</h1> <% form_for(@activo) do |f| %> <%= f.error_messages %> <%= render :partial => "form", :locals => {:f => f, :label_text => "Crear"} %> <% end %> <%= link_to 'Atrás', activos_path %> edit.html.erb: <h1>Editar activo</h1> <% form_for(@activo) do |f| %> <%= f.error_messages %> <%= render :partial => "form", :locals => {:f => f, :label_text => "Actualizar"} %> <% end %> <%= link_to 'Ver', @activo %> | <%= link_to 'Atrás', activos_path %> Podemos notar como la diferencia entre los dos códigos radica básicamente en el título de la página y en el nombre del botón, que en un caso se pasa el parámetro “Crear” y en el de editar se pasa el parámetro “Actualizar”. Además en el caso de “editar” se agrega un enlace a la página “ver” 57 para mostrar el contenido actual del estado que se esta editando. El resultado de esto es una página como la que se muestra a continuación: Figura 3. Vista para creación de activo. Otra de las vistas corresponde a la de “mostrar” que se encarga de presentar en pantalla los valores de las diferentes variables de un activo específico creado anteriormente. show.html.erb: <% form_tag({:controller => 'activos', :action => 'index'}, :method => 'get') do %> <%= text_field_tag "q", params[:q]|| '# de serie o placa '%> <%= submit_tag "Buscar!" %> <% end %> El código anterior inicia creando un campo de texto para ingresar los parámetros de una búsqueda. Este se crea como un forma que llama al método listar de el controlador de activos, pasándole a este el parámetro de la búsqueda. Además se incluye un texto por defecto “# de serie o placa” para indicar al usuario que esos son los parámetros que se utilizan para la búsqueda. <p> <b>Num serie:</b> <%=h @activo.num_serie %> </p> <p> <b>Placa:</b> 58 <%=h @activo.placa %> </p> Ahora se muestra un encabezado para el número de serie y la placa del activo, desplegando luego el valor de esa variable que se llama mediante la sentencia “@activo.placa”. <p> <b>Modelo:</b> <%=h @activo.modelo.nombre_modelo %> </p> <p> <b>Estado:</b> <%=h @activo.estado.nombre %> </p> En el caso de el modelo y el estado para el activo, sabemos que en la tabla de activos se almacena únicamente el número de identificación de estos, mediante una llave foránea. Por lo tanto para lograr deplegar el nombre del modelo y de el activo es necesario realizar una llamada a las herencias de ese activo como lo son “@activo.modelo” y “@activo.estado” y llamar ahora si al nombre de cada uno de ellos para ser deplegado. <% if current_user.has_role?('administrator') %> <%= link_to 'Editar', edit_activo_path(@activo) %> | <%= link_to 'Atras', activos_path %> <% end %> Finalmente se evalúa si el usuario actual es administrador y en ese caso se despliegan los enlaces para poder editar dicho activo y para volver a la página anterior. El código despliega una página como la siguiente: 59 Figura 4. Vista para edición de activo. La última vista para los activos corresponde a la de listar los activos. En esta se listan una serie de activos según los parámetros que se hayan especificado. La lista puede ser para una marca específica para un estado determinado o simplemente puede ser una lista de activos que correspondan a un parámetro de búsqueda ingresado por el usuario. Index.html.erb: <% form_tag({:controller => 'activos', :action => 'index'}, :method => 'get') do %> <%= text_field_tag "q", params[:q]|| '# de serie o placa '%> <%= submit_tag "Buscar!" %> <% end %> Inicialmente se crea un campo de texto para búsquedas igual al comentado en el caso de el archivo de “mostrar”. <% if params[:modelo_id].nil? and params[:estado_id].nil? %> <h1>Lista de activos</h1> <% else if params[:modelo_id].nil? %> <h1>Lista de activos en estado "<%=h Estado.find(params[:estado_id]).nombre %>"</h1> <% else %> <h1>Lista de activos para el modelo "<%=h Modelo.find(params[:modelo_id]).nombre_modelo%>"</h1> <% end %> <% end %> El encabezado de la página puede variar dependiendo de el tipo de activos que se despliega en la página, por lo tanto se realiza una lógica para determinar si existe algún parámetro de tipo de modelo o de estado, para de esa manera especificar en la página que la lista de activos es para dicho 60 parámetro específico. Si no existen se despliega simplemente el mensaje “Lista de activos”. <table border="1"> <tr> <th>Num serie</th> <th>Placa</th> <th>Modelo</th> <th>Descripción</th> <th>Estado</th> <th>Marca</th> </tr> Se crea ahora una tabla y el nombre de sus columnas, para cada uno de los parámetros del activo. <% for activo in @activos %> <tr> <td><%= link_to activo.num_serie, activo %></td> <td><%=h activo.placa %></td> <td><%= link_to %Q{#{activo.modelo.nombre_modelo}}, {:action => 'index', :modelo_id => activo.modelo_id} %></td> <td><%= activo.modelo.descripcion %></td> <td><%= link_to %Q{#{activo.estado.nombre}}, {:action => 'index', :estado_id => activo.estado_id} %></td> <td><%= link_to %Q{#{activo.modelo.marca.nombre_marca}}, {:action => 'index', :marca_id => activo.modelo.marca_id} %></td> <% if current_user.has_role?('administrator') %> <td><%= link_to 'Eliminar', activo, :confirm => '¿Está seguro que desea eliminar este elemento?', :method => :delete %></td> <% end %> </tr> <% end %> </table> La lista se llena mediante una sentencia “for” de todos los activos encontrados para ese parámetro. Se agrega además un enlace al número de serie del activo de manera que al presionar sobre este se despliegue la página donde se muestra dicho activo. De igual manera se crea un enlace en el nombre del modelo y de el estado de manera que al presionar sobre ellos se llama al método listar y se pasa como parámetro el identificador de ese parámetro. Esto permite desplegar solamente los activos que pertenezcan al modelo seleccionado o que tengan el estado seleccionado según sea el caso. Se incluye además si el usuario es administrador un enlace para borrar cada activo, además 61 se agraga una confirmación de esta acción para evitar que se elimine un activo por error. <%= will_paginate @activos %> <br /> <% if current_user.has_role?('administrator') %> <%= link_to 'Crear activo', {:action => 'new', :modelo_id => params[:modelo_id]} %> <% end %> Finalmente se agrega una sentencia para indicar que se debe desplegar en varias páginas los activos para evitar que se recargue mucho las páginas y realizar la búsqueda de un activo específico de una manera más cómoda. Luego se verifica si el usuario es administrador y si es así se incluye un enlace para crear un activo con el parámetro del modelo seleccionado en caso de existir. Finalmente la página desplegada es la siguiente: Figura 5. Vista de listado de activos. 7.2 Vistas del sistema de autenticación de usuarios. Tenemos en esta sección las vistas para los controladores correspondientes a usuarios, cuentas, roles, sesiones, y contraseñas. Como se mencionó en el capítulo de controladores, algunos de los métodos comunes no están presentes en muchos de los casos, debido a que no son necesarios o por seguridad no fueron definidos. 62 Para los usuarios tenemos las vistas para crear, editar, listar y mostrar usuarios. En el caso de listar se utilizó un parcial para separar el código, de manera que en este se realice únicamente la parte que despliega los usuarios, mientras que el resto de la página se completa en el siguiente archivo. index.html.erb (users): <% form_tag({:controller => 'users', :action => 'index'}, :method => 'get') do %> <%= text_field_tag "q", params[:q]|| 'Buscar Usuario '%> <%= submit_tag "Buscar!" %> <% end %> Primero se agrega un cuadro de texto para ingresar parámetros de búsqueda, iguales a los mencionados para el caso de los activos. <h2>Lista de usuarios</h2> <table border="1"> <tr> <th>Usuario</th> <th>Nombre</th> <th>Apellido</th> <th>Carné</th> <th>Roles</th> </tr> <%= render :partial => 'user', :collection => @users %> </td> Se crea una tabla con algunos de los parámetros de los usuarios. Luego se especifica que se debe completar con el código del parcial “user”. <%= link_to 'Crear_usuario', new_user_path %> Se crea luego un enlace para permitir crear nuevos usuarios. _user.html.erb: <tr class="<%= cycle('odd', 'even') %>"> <td><%= link_to user.login, user_path(user) %></td> <td><%=h user.nombre %></td> <td><%=h user.apellido %></td> <td><%=h user.carne %></td> </td> La tabla se completa con los datos de los usuarios como se muestra anteriormente. No todos los datos son desplegados para evitar que la tabla se haga muy ancha e incómoda. Por lo tanto se 63 despliegan únicamente los datos más significativos. El resto de los datos se pueden observar presionando sobre el enlace de el nombre de usuario el cual dirige hacia la página de “mostrar” dicho usuario. <% if user == current_user %> <td>Usuario actual</td> <%else%> <td><%= link_to 'Editar roles', user_roles_path(user) %></td> <td><%= link_to 'Eliminar', user, :confirm => '¿Esta seguro que desea eliminar el usuario?', :method => :delete %></td> <% end %> </tr> Para finalizar la tabla se crean enlaces a editar roles y eliminar el usuario. Estos enlaces no se despliegan en el caso del usuario actual porque sería un error grave eliminar o cambiar el rol a un usuario administrador ya que perdería la capacidad de ingreso de nuevo al sistema o su cualidad de administrador. Esta vista se presenta a continuación: Figura 6. Vista de listado de usuarios. La vista “editar” permite editar los datos personales de el usuario, sin embargo el cambio de contraseñas lo realiza otro controlador por lo tanto estos parámetros no se modifican en esta vista. La vista “nuevo” y “mostrar” son muy similares a las mostradas anteriormente. En la vista “nuevo” si se crea una forma para introducir todos los datos del usuario, incluyendo el nombre de usuario en el sistema (login) y la contraseña la cual tiene un campo de confirmación para evitar 64 introducir un error en la contraseña. El cambio de una contraseña lo realiza el controlador “accounts_controller.rb” por lo tanto se tiene una vista separada para esto: edit.html.erb(accounts): <% form_tag url_for(:action => "update") do %> <p><label for="Contraseña anterior" class="block">Contraseña anterior</label><br /> <%= password_field_tag 'old_password', @old_password, :size => 45 %></p> <p><label for="Contraseña nueva" class="block">Contraseña nueva</label><br /> <%= password_field_tag 'password', {}, :size => 45 %><br /> <small>Entre 4 y 40 caracteres</small></p> <p><label for="Confirmacion de contraseña nueva" class="block">Confirmacion de contraseña nueva</label><br /> <%= password_field_tag 'password_confirmation', {}, :size => 45 %></p> <%= submit_tag 'Cambiar contraseña' %> <% end %> Esta forma permite el cambio de contraseñas. Se despliega un cuadro te texto para la contraseña vieja, luego se despliega uno para la contraseña nueva y otro para su confirmación. Además se despliega un texto indicando al usuario que la contraseña debe estar entre 4 y 40 caracteres. Finalmente se coloca un botón para enviar los datos. La vista se presenta a continuación: Figura 7. Vista de edición de contraseña. Los roles son creados y destruidos únicamente desde el código fuente, por lo tanto no existe vistas para crear ni editar estos. La única vista disponible para los roles es la de listar los roles, la 65 cual permite ver una lista de los roles para un usuario específico y añadir o quitar roles a este. Para esto se utilizó por comodidad de programación un parcial llamado “_role.html.erb”. _role.html.erb: <li> <%= role.rolename %> <% if @user.has_role?(role.rolename) %> <%= link_to 'Remover rol', user_role_url(:id => role.id, :user_id => @user.id), :method => :delete %> <% else %> <%= link_to 'Agregar rol', user_role_url(:id => role.id, :user_id => @user.id), :method => :put %> <% end %> </li> Este código verifica para cada código si este esta asignado al usuario, en cuyo caso se agrega un enlace a “remover rol”, si el rol no esta asignado al usuario se incluye un enlace para agregar el rol al usuario. index.html.erb (roles): <h2>Roles para <%=h @user.login.capitalize %></h2> <h3>Roles asignados:</h3> <ul><%= render :partial => 'role', :collection => @user.roles %></ul> <h3>Roles disponibles:</h3> <ul><%= render :partial => 'role', :collection => (@all_roles ­ @user.roles) %></ul> El código anterior muestra el nombre del usuario para el cual se estan desplegando los roles, luego muestra por separado los roles asignados al usuario y los roles disponibles de la siguiente manera: Figura 8 . Vista de listado de roles asignados. 66 Figura 9. Vista de listado de roles no asignados. La vista para crear una sesión corresponde a la forma de ingreso al sistema donde se le solicita al usuario su nombre de usuario y su contraseña para poder conectarse al sistema. El archivo presenta esta forma: <center><h1>Introduzca su nombre de usuario y contraseña</h1> <% form_tag session_path do ­%> <p><label for="login">Usuario</label><br/> <%= text_field_tag 'login' %></p> <p><label for="password">Contraseña</label><br/> <%= password_field_tag 'password' %></p> <p><label for="remember_me">Remember me:</label> <%= check_box_tag 'Recordarme en este equipo' %></p> <p><%= submit_tag 'Entrar' %></center> <% end ­%> Como podemos ver se muestra inicialmente un encabezado indicando que se debe ingresar el nombre de usuario y la contraseña, Luego se agregan dos campos de texto para el nombre de usuario y la contraseña. 67 Además se proporciona la opción de ser recordado en el equipo mediante una casilla de verificación. Finalmente se agrega un botón para enviar los datos e ingresar al sistema en caso de que sean correctos. 7.3 Plantilla genérica para vistas. Las vistas de los apartados anteriores sirven para desplegar los datos de la base de datos y realizar cambios en esta. Otra parte de las vistas permite darle un aspecto más estético a la página, mientras que proporciona algunos menús para movilizarse fácilmente entre páginas. Esta plantilla permite crear encabezados a la página, así como barras laterales entre otras cosas. La plantilla creada para el proyecto contiene un encabezado con el título del sistema y una barra lateral donde se puede navegar a través de las diferentes páginas de la aplicación. application.html.erb: <title>Sistema de inventario EIE </title> <%= stylesheet_link_tag 'inventario' %> </head> <body id= "inventario"> El código anterior especifica el nombre que tendrá la página en la barra de título del explorador web. Luego se especifica el nombre del archivo de hojas de estilos que se utilizará que corresponde a “inventario.css”. <div id= "banner"> <%= image_tag ("rails.png")%> <%= @page_title||"Inventario de Bodega ­­ EIE" %> </div> Se crea la primera división de la página. Esta corresponde a el encabezado de la página. En este se agrega una imagen con el símbolo de el framework utilizado. Después se agrega un texto que hará las de título de la aplicación. Se selecciono como título “Inventario de Bodega – EIE”. A esta 68 división se le da el identificador “banner”. <div id="side"> <h1> Enlaces </h1> <% if logged_in? %> <li><%= link_to 'Activos', activos_path %> <br /></li> <li><%= link_to 'Marcas', marcas_path %> <br /></li> <li><%= link_to 'Modelos', modelos_path %> <br /></li> <% if current_user.has_role?('administrator') %> <li><%= link_to 'Estados', estados_path %> </li> <% end %> <br /><br /> <li>Logged in as:</li> (<%= link_to h(current_user.login.capitalize), user_path(current_user) %>)<br /><br /> <%= link_to 'Editar cuenta', edit_user_path(current_user) %><br /> <%= link_to 'Cambiar contraseña', change_password_path %><br /> <%= link_to 'Desconectarse', logout_url %><br /> <% if current_user.has_role?('administrator') %><br /> <li><%= link_to 'Administrar usuarios', users_path %></li> <% end %> <% else %> <li><%= link_to 'Entrar', new_session_path %></li> <% end %> <br /> </div> La segunda división corresponde a la barra lateral y es denominada “side”. Esta contiene el menú que permite a los usuarios seleccionar las paginas deseadas. Se evalúa si un usuario esta conectado. Si lo esta se despliega en el menú los enlaces para ver activos, marcas y modelos. Si además el usuario es administrador se agrega el enlace de estados. Luego se despliega el nombre del usuario conectado así como enlaces para editar cuenta, cambiar contraseña y desconectarse del sistema. Una vez más si el usuario es administrador se agrega la opción de administrar usuarios. En el caso en que el usuario no este conectado al sistema simplemente se despliega un enlace a “Entrar” que envía a la página de conexión al sistema. <div id="main"> <% if flash[:notice] %> <%= "<div class='notices'>" + flash[:notice] + "</div>" %> <% end %> <% if flash[:error] %> 69 <%= "<div class='error'>" + flash[:error] + "</div>" %> <% end %> <%= yield %> </div> </div> La última división de la página corresponde al espacio donde se despliegan las vistas para todas las acciones y contenidos de la base de datos que fueron explicadas en los apartados anteriores. Se indica primero un par de sentencias para que se desplieguen, en caso de existir, mensajes de notificación o de error. Luego mediante la sentencia “<%= yield %> ” se indica que se desplieguen la información de las otras vistas. El último archivo que compone las vistas corresponde al archivo de hoja de estilos que permite especificar el formato de cada una de las partes de la página. En este se especifican colores, tipos de letra, tamaño de las divisiones de la página, alineaciones del texto entre otras cosas. Este archivo se encuentra en la ruta “public/stylesheets” y es denominado “inventario.css”. La explicación de el código de este archivo no se considera de importancia por lo que se omite. A continuación se muestra la estructura de la página tanto para un usuario conectado como para uno sin conectarse. 70 Figura 10. Vista final del sistema para un usuario conectado. Figura 11. Vista final del sistema para un usuario desconectado. 71 CAPÍTULO 8: Traducciones. Los mensajes de error que se presentan a la hora de realizar la validación de los datos son mensajes genéricos desplegados por Ruby on Rails. Como se puede suponer estos mensajes están por defecto en inglés, por lo que fue necesario realizar un “parche” para realizar la traducción de estos mensajes. Se tradujo además la forma en la que se despliegan las fechas y tiempos restantes. Para realizar esto se creó una carpeta llamada “overrides” en la cual se albergaron estos archivos de traducciones que se comentan a continuación: errors.rb module ActiveRecord class Errors begin @@default_error_messages = { :inclusion => "no está incluido en la lista", :exclusion => "está reservado", :invalid => "no es válido", :confirmation => "no es una confirmación", :accepted => "debe ser aceptado", :empty => "no puede estar vacío", :blank => "no puede estar en blanco", :too_long => "es demasiado largo (máximo %d caracteres)", :too_short => "es demasiado corto (mínimo %d caracteres)", :wrong_length => "no tiene la longitud correcta (debería tener %d caracteres)", :taken => "ya existe en el sistema", :not_a_number => "debe ser un número" } end end end El módulo anterior modifica la clase “errors” que se encarga de manejar los mensajes de error para diferentes situaciones. Se puede observar como se presentan cambios en diferentes tipos de error, como el caso de que un valor sea inválido, que no puede estar vacío o que la longitud no esta en el rango permitido. module ActionView #nodoc module Helpers module ActiveRecordHelper def error_messages_for(object_name, options = {}) options = options.symbolize_keys object = instance_variable_get("@#{object_name}") unless object.errors.empty? 72 content_tag("div", content_tag(options[:header_tag] || "h2", "Hay errores que impiden guardar el registro") + content_tag("p", "Compruebe los siguientes campos:") + content_tag("ul", object.errors.full_messages.collect { |msg| content_tag("li", msg) }), "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation" ) end end end end end El módulo anterior se encarga de presentar los encabezados de los mensajes de error. Cuando existen errores se despliegan los mensajes “Hay errores que impiden guardar en el registro” y “Compruebe los siguientes campos”. date.rb: require 'date' class Date remove_const(:MONTHNAMES) MONTHNAMES = [nil] + %w(Enero Febrero Marzo Abril Mayo Junio Julio Agosto Septiembre Octubre Noviembre Diciembre) remove_const(:ABBR_MONTHNAMES) ABBR_MONTHNAMES = [nil] + %w(Ene Feb Mar Abr May Jun Jul Ago Sep Oct Nov Dic) remove_const(:DAYNAMES) DAYNAMES = %w(Domingo Lunes Martes Miercoles Jueves Viernes Sábado) remove_const(:ABBR_DAYNAMES) ABBR_DAYNAMES = %w(Dom Lun Mar Mié Jue Vie Sáb) end Este módulo realiza el cambio al español de los nombres de los meses y los días, además de las abreviaciones de los días. class Time alias :strftime_nolocale :strftime def strftime(format) format = format.dup format.gsub!(/%a/, Date::ABBR_DAYNAMES[self.wday]) format.gsub!(/%A/, Date::DAYNAMES[self.wday]) format.gsub!(/%b/, Date::ABBR_MONTHNAMES[self.mon]) format.gsub!(/%B/, Date::MONTHNAMES[self.mon]) self.strftime_nolocale(format) end end Este módulo cambia el formato de la fecha a un formato latino en el que se despliega primero el día seguido por el més y el año. module ActionView 73 module Helpers module DateHelper def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false) from_time = from_time.to_time if from_time.respond_to?(:to_time) to_time = to_time.to_time if to_time.respond_to?(:to_time) distance_in_minutes = (((to_time ­ from_time).abs)/60).round distance_in_seconds = ((to_time ­ from_time).abs).round case distance_in_minutes when 0..1 return (distance_in_minutes == 0) ? 'menos de un minuto' : '1 minuto' unless include_seconds case distance_in_seconds when 0..4 then 'menos de 5 segundos' when 5..9 then 'menos de 10 segundos' when 10..19 then 'menos de 20 segundos' when 20..39 then 'medio minuto' when 40..59 then 'menos de un minuto' else '1 minuto' end when 2..44 then "#{distance_in_minutes} minutos" when 45..89 then 'aprox. 1 hora' when 90..1439 then "aprox. #{(distance_in_minutes.to_f / 60.0).round} horas" when 1440..2879 then '1 día' when 2880..43199 then "#{(distance_in_minutes / 1440).round} días" when 43200..86399 then 'aprox. 1 mes' when 86400..525599 then "#{(distance_in_minutes / 43200).round} months" when 525600..1051199 then 'aprox. 1 año' else "over #{(distance_in_minutes / 525600).round} years" end end end end end En este módulo se definen las expresiones para los casos de distancia de tiempo. Se definen primero las distancias a partir de el número de segundos, desde 5 segundos hasta 1 minuto. Luego se definen las distancias según los minutos, que van desde unos minutos hasta distancias de varios años. Para esto se establecen algunos rangos en los que se considera cada condición, por ejemplo, entre 45 minutos y 89 minutos se considera una distancia de aproximadamente 1 hora. Finalmente estos archivos son incluidos en el archivo de configuración “environment.rb”. De esta manera la aplicación sabe que debe cargar estos archivos a la hora de correr el servidor, de manera que el “parche” quede funcionando. 74 CAPÍTULO 9: Pruebas. Las pruebas realizadas al sistema consistieron básicamente en pruebas para comprobar la integridad de los datos. Además se realizaron algunas pruebas para comprobar el funcionamiento correcto de las búsquedas . 9.1 Pruebas de validación. Las pruebas de validación se realizaron para comprobar el buen funcionamiento de la validación de los datos. En el caso de los activos se especifico que la placa debía ser un valor único, es decir que no puede estar registrada anteriormente en el sistema, esto evita que se ingresen al sistema equipos que ya han sido registrados anteriormente por lo que se evita la duplicación de datos. Se intentó entonces introducir un valor de placa ya existente, teniendo el siguiente resultado. Figura 12. Prueba de validación de placa existente. Al intentar ingresar una placa ya existente el sistema lanza el mensaje de error indicando que la placa ya existe, además se evita la creación del activo. Además se conservan los calores ingresados por el usuario de manera que los pueda modificar fácilmente además de corroborar cual fue el valor que produjo el error. 75 Otra prueba de validación realizada consiste en tratar de crear un activo con alguno de los campos en blanco: Figura 13. Prueba de validación de número de serie en blanco. Figura 14. Prueba de validación de placa en blanco. Como se observa se trato de dejar el número de serie y la placa en blanco. En ambos casos el sistema notificó el error indicando la casilla que debe tener un valor. La última prueba para los activos consiste en considerar un número de placa que contenga caracteres diferentes a números. Esto debido a que se supone que los números de placa deben ser estrictamente numéricos. La siguiente figura muestra el resultado: 76 Figura 15. Prueba de validación de placa no numeral. Una vez más el sistema indica que existe un error en la placa ya que esta debe ser un número y en la prueba se intentó ingresar un carácter. En el caso de los usuarios las pruebas de validación fueron similares, obteniéndose prácticamente los mismos resultados por lo que es repetitivo comentarlo. 9.2 Búsquedas. Las búsquedas se habilitaron en el caso de los activos, los modelos , las marcas y los usuarios. Se probó entonces el funcionamiento de estas en cada uno de los casos según el parámetro de búsqueda especificado. En el caso de los activos las búsquedas se pueden realizar por número de serie o por placa. El resultado de ambos casos es el siguiente: Figura 16. Prueba de búsqueda de activo por número de serie. 77 Figura 17. Prueba de búsqueda de activo por placa. Se observa como los resultados coinciden con la búsqueda realizada en los dos casos. Además se probó utilizando letras mayúsculas y minúsculas y se determino que las búsquedas no son sensitivas a mayúsculas y minúsculas lo cual es satisfactorio ya que se desea que se detecten la mayor cantidad de elementos con cada búsqueda, coincidentes con el parámetro. Las pruebas de búsqueda realizadas para el caso de las marcas, modelos y usuarios tuvieron resultados similares, por lo que fueron muy satisfactorias. 78 CAPÍTULO 10: Conclusiones y recomendaciones. 10.1 Conclusiones. ● El desarrollo de aplicaciones con una herramienta como Ruby on Rails es sencillo debido a que evita al programador tener que programar cosas que ya han sido desarrolladas, permitiendo a este concentrarse realmente en las partes que son esenciales según los requerimientos de la aplicación que debe desarrollar. ● El patrón modelo­vista­controlador permite un mayor orden y una independencia entre las partes de la aplicación, brindando a cada una de ellas una función especifica diferente a las funciones de las otras partes del programa. Además permite que el entendimiento del código sea más sencillo. ● Un sistema computarizado de base de datos para almacenar activos de bodega permite un manejo confiable de la información, además facilita realizar cambios en el inventario, de manera que se puede considerar como una gran mejora con respecto a inventarios almacenados en papel. ● Las pruebas realizadas al sistema fueron satisfactorias por lo que se puede confiar en el funcionamiento correcto del sistema. La validación de los datos es la correcta, y puede ser modificada según las directivas deseadas por el encargado de bodega que va a utilizar el sistema. ● El sistema desarrollado cumple con todas las especificaciones y requerimientos que fueron solicitados por lo que se puede utilizar para sustituir el sistema actual de almacenamiento de información de inventario de la bodega de la Escuela de Ingeniería Eléctrica de la Universidad de Costa Rica. 79 10.2 Recomendaciones. ● Se podría agregar al sistema una sección para manejo de componentes electrónicos existentes en bodega, de manera que los estudiantes puedan ingresar al sistema y revisar los elementos que tienen disponibles para realizar sus laboratorios. Estos elementos como resistencias, condensadores, integrados, etc se deben manejar en tablas diferentes, ya que no poseen los mismos atributos que los equipos y es muy difícil llevar control de la cantidad existente, sino únicamente de su existencia en la bodega. ● El sistema puede completarse con un sistema de manejo de préstamos a los estudiantes, que permita a un estudiante reservar equipos de forma remota y al administrador llevar un registro digital de los prestamos que se han realizado. Para esto se puede crear una tabla que contenga la relación entre los usuarios y los equipos que han sido prestados a estos. La modificación de la base de datos se puede realizar fácilmente mediante migraciones de manera que se mantenga la información existente en ella. 80 BIBLIOGRAFÍA: [1] Korth H. F. , “Fundamentos de bases de datos”, Segunda edicion, McGraw­Hill / Interamericana de España S. A. , México, 1993. [2] Wikipedia. “Base de Datos”, Wikipedia, http://es.wikipedia.org/wiki/Base_de_datos [3] Wikipedia. “Sistemas Gestores de Base de Datos”, Wikipedia, http://es.wikipedia.org/wiki/Sistemas_gestores_de_bases_de_datos, Setiembre, 2008. [4] Wise, B. “Normalización de bases de datos y técnicas de diseño”, http://www.bulma.net/body.phtml?nIdNoticia=483, 2001. [5] Wikipedia. “Ruby”, Wikipedia, http://es.wikipedia.org/wiki/Ruby, Setiembre, 2008. [6] Wikipedia. “Ruby on Rails” Wikipedia, http://es.wikipedia.org/wiki/Ruby_on_Rails, Setiembre, 2008. [7] ­­­ “Ruby Tutorial”, http://rubytutorial.wikidot.com/introduccion, Junio, 2008. [8] ­­­ “Manual del Usuario de PostgreSQL”, http://es.tldp.org/Postgresql­es/web/navegable/user/user.html [9] Wikipedia. “Modelo­Vista­Controlador” ,Wikipedia, http://es.wikipedia.org/wiki/Modelo_Vista_Controlador, Setiembre, 2008. [10] ­­­”Patrón “Modelo­controlador­vista”, http://www.proactiva­calidad.com/java/patrones/mvc.html, Octubre, 2008. [11] Jesús Dugarte “Rails en español”, http://jdugarte.blogspot.com/2008/04/rails­en­espaol.html, Abril, 2008. 81