UNIVERSIDAD CENTRAL DEL ECUADOR FACULTAD DE INGENIERÍA EN CIENCIAS FÍSICAS Y MATEMÁTICA CARRERA DE INGENIERÍA INFORMÁTICA ANÁLISIS DESCRIPTIVO DE LA TECNOLOGÍA RUBY ON RAILS PARA EL DESARROLLO DE PÁGINAS WEB TRABAJO DE GRADUACIÓN PREVIO A LA OBTENCIÓN DEL TÍTULO DE INGENIERO INFORMÁTICO AUTOR: ALEX FERNANDO PICO COBA TUTOR: M.Sc. RENÉ ALFONSO CARRILLO FLORES QUITO – 24 DE MAYO 2016 DEDICATORIA A mi madre Martha Coba con cariño. ii AUTORIZACIÓN DE LA AUTORÍA INTELECTUAL Yo Alex Fernando Pico Coba en calidad de autor del trabajo de investigación: “Análisis descriptivo de la tecnología Ruby on Rails para el desarrollo de páginas web”, autorizo a la Universidad Central del Ecuador hacer uso de todos los contenidos que me pertenecen o parte de los que contiene esta obra, con fines estrictamente académicos o de investigación. Los derechos que como autores me corresponden, con excepción de la presente autorización, seguirán vigentes a mi favor, de conformidad con lo establecido en los artículos 5, 6, 8; 19 y demás pertinentes de la Ley de Propiedad Intelectual y su Reglamento. Así mismo, autorizo a la Universidad Central del Ecuador para que realice la digitalización y publicación de este trabajo de investigación en el repositorio virtual, de conformidad a lo dispuesto en el Art. 144 de la Ley Orgánica de Educación Superior. En la ciudad de Quito, a los días 23 del mes mayo de 2016 Alex Fernando Pico Coba CC: 1803990397 Telf: 0984141535 Dirección electrónica: [email protected] iii CERTIFICACIÓN DEL TUTOR Yo, Rene Alfonso Carrillo Flores en calidad de tutor del trabajo de titulación: “Análisis Descriptivo de la Tecnología Ruby on Rails para el Desarrollo de Páginas Web”, elaborado por el estudiante de la Carrera de Ingeniería Informática, Facultad de Ciencias Físicas y Matemática de la Universidad Central de Ecuador, considero que el mismo reúne los requisitos y méritos en el campo metodológico, para ser sometido a la evaluación por parte del jurado examinador que se designe por lo que lo APRUEBO, a fin de que el trabajo investigativo sea habilitado para continuar con el proceso de titulación determinado por la Universidad Central del Ecuador. En la ciudad de Quito, a los días 01 del mes abril de 2016 Ing. Rene Alfonso Carrillo Flores M.Sc. CI: 1709140378 iv CERTIFICACIÓN DE REVISORES v CALIFICACIONES vi CONTENIDO DEDICATORIA ....................................................................................................................... ii AUTORIZACIÓN DE LA AUTORÍA INTELECTUAL ..............................................................iii CERTIFICACIÓN DEL TUTOR ............................................................................................. iv CERTIFICACIÓN DE REVISORES ....................................................................................... v CALIFICACIONES ................................................................................................................. vi LISTA DE ANEXOS ................................................................................................................ x LISTA DE FIGURAS.............................................................................................................. xi LISTA DE TABLAS ............................................................................................................... xiii RESUMEN ............................................................................................................................xiv ABSTRACT........................................................................................................................... xv INTRODUCCIÓN .................................................................................................................... 1 1. MARCO TEÓRICO .................................................................................................. 3 1.1 Ruby on Rails ........................................................................................................... 3 1.1.1 Ruby ......................................................................................................................... 3 1.1.2 Rails ......................................................................................................................... 3 1.1.3 Ruby gem ................................................................................................................. 4 1.2 Arquitectura MVC ..................................................................................................... 4 1.2.1 Modelo...................................................................................................................... 5 a) Active Record ........................................................................................................... 5 b) Migraciones .............................................................................................................. 7 c) Validaciones ............................................................................................................. 9 d) Callback ................................................................................................................. 15 e) Asociaciones .......................................................................................................... 17 vii 1.2.2 View (Vistas) .......................................................................................................... 24 1.2.3 Controlador (Controller) ......................................................................................... 31 2. METODOLOGÍA DE DESARROLLO .................................................................... 37 2.1 Tipo de la Investigación ......................................................................................... 37 2.2 Población ............................................................................................................... 37 2.3 Método de Investigación ........................................................................................ 38 3. CALCULOS Y RESULTADOS .............................................................................. 39 3.1 Paquete de Instalación .......................................................................................... 39 3.2 Instalación y configuración .................................................................................... 39 3.3 Mi primera aplicación ............................................................................................. 41 3.3.1 Creación de los recursos ....................................................................................... 41 3.3.2 Poner en marcha el servidor Web ......................................................................... 43 3.3.3 Hola, Rails .............................................................................................................. 45 3.4 Aplicación web completa ....................................................................................... 47 3.4.1 Creación de Modelos ............................................................................................. 49 3.4.2 Modelo uno a muchos ........................................................................................... 51 3.4.3 Modelo muchos a muchos ..................................................................................... 52 3.4.4 Actualizar o crear campos ..................................................................................... 53 3.4.5 Crear Campos para archivos ................................................................................. 55 3.4.6 Manejo de Vistas ................................................................................................... 58 3.4.7 Assets..................................................................................................................... 63 3.4.8 Validaciones ........................................................................................................... 65 3.4.9 Buscador y paginación .......................................................................................... 66 3.4.10 Autenticación de usuario ....................................................................................... 68 4. DISCUSIÓN ........................................................................................................... 74 viii 5. CONCLUSIONES .................................................................................................. 79 6. RECOMENDACIONES .......................................................................................... 81 BIBLIOGRAFÍA ..................................................................................................................... 83 ANEXOS ............................................................................................................................... 85 ix LISTA DE ANEXOS ANEXO A Device ................................................................................................................................87 ANEXO B Cargar aplicación al servidor ............................................................................................89 ANEXO C Configurar Correo Electrónico ..........................................................................................90 ANEXO D Carrito Básico de Compras ...............................................................................................92 ANEXO E Diagrama de Flujo ...........................................................................................................104 x LISTA DE FIGURAS Figura 1. Asociación belong_to (Dev & Noria, s.f.) .............................................................. 19 Figura 2. Asociación has_one (Dev & Noria, s.f.) ................................................................ 19 Figura 3. Asociación has_many (Dev & Noria, s.f.) ............................................................. 20 Figura 4. Asociación has_many :through (Dev & Noria, s.f.) .............................................. 20 Figura 5. Asociación has_one :through (Dev & Noria, s.f.) ................................................. 21 Figura 6. Asociación has_and_belongs_to_many (Dev & Noria, s.f.) ................................. 22 Figura 7. Asociaciones polimórficas (Dev & Noria, s.f.) ...................................................... 23 Figura 8. Ventana de instalación de Ruby/Rails .................................................................. 39 Figura 9. Ventana para seleccionar la ubicación ................................................................. 40 Figura 10. Directorio AppWeb .............................................................................................. 42 Figura 11. WEBrick ............................................................................................................... 44 Figura 12 Hola Rails! ............................................................................................................ 46 Figura 13. Modelo Entidad Relación .................................................................................... 49 Figura 14. Vista Tipos de Productos .................................................................................... 50 Figura 15. Vista de Fabricante ............................................................................................. 50 Figura 16. Vista Producto ..................................................................................................... 51 Figura 17. Vista Proveedor................................................................................................... 52 Figura 18. ImageMagick ....................................................................................................... 55 Figura 19. ImageMagick Tasks ............................................................................................ 55 Figura 20. Vista Fabricante Editada ..................................................................................... 58 Figura 21. Vista Fabricante Show ........................................................................................ 59 Figura 22. Vista Fabricante Index ........................................................................................ 59 Figura 23. Vista Producto Editada ....................................................................................... 61 Figura 24. Vista Producto Show........................................................................................... 62 Figura 25. Vista Producto Index ........................................................................................... 63 Figura 26. Menu de Navegación .......................................................................................... 65 Figura 27. Buscador ............................................................................................................. 68 Figura 28. Inicio de Sesión ................................................................................................... 70 Figura 29. Autenticación ....................................................................................................... 71 Figura 30. Aplicación aplicado estilos .................................................................................. 73 Figura 31 Producto Home .................................................................................................... 92 Figura 32 Añadir Carrito ....................................................................................................... 96 Figura 33 Mis compras ....................................................................................................... 102 Figura 34 Super Usuario .................................................................................................... 104 Figura 35 Un diagrama de flujo a nivel de contexto .......................................................... 105 xii LISTA DE TABLAS Tabla 1. Rutas de recursos (Dev & Noria, s.f.) .................................................................... 35 Tabla 2. Archivos y Carpetas creadas ................................................................................. 43 xiii RESUMEN ANÁLISIS DESCRIPTIVO DE LA TECNOLOGÍA RUBY ON RAILS PARA EL DESARROLLO DE PÁGINAS WEB Autor: Alex Fernando Pico Coba Tutor: René Alfonso Carrillo Flores El presente proyecto es una guía para el desarrollo de aplicaciones web utilizando la tecnología Ruby on Rails, describe paso a paso con un ejemplo y presenta información de esta herramienta para el uso e inducción al desarrollo de aplicaciones web. Es uno de los primeros trabajos en el Ecuador que recolecta elementos básicos para la introducción al desarrollo de aplicaciones web utilizando Ruby on Rails. PALABRAS CLAVES: / LENGUAJE RUBY/ FRAMEWORK RAILS/ ACTIVE RECORD/ CONVENCIÓN SOBRE CONFIGURACIÓN / APLICACIÓN WEB / ARQUITECTURA MVC xiv ABSTRACT DESCRIPTIVE ANALYSIS OF RUBY ON RAILS TECHNOLOGY FOR WEB PAGES DEVELOPMENT Autor: Alex Fernando Pico Coba Tutor: René Alfonso Carrillo Flores This project is a guide to develop web applications using Ruby on Rails technology, it describes step by step with an example and present an information of this tool in order to use and induction to web application development. It is one of the first works in Ecuador that collect basic elements for introduction to web aplication development using Ruby on Rails. KEYWORDS: / RUBY LANGUAGE / RAILS FRAMEWORK / ACTIVE RECORD / CONVENTION OVER CONFIGURATION / WEB APPLICATION / MVC ARCHITECTURE I CERTIFY that the above and foregoing is a true and correct translation of the original document in Spanish. Lcdo. Diego Mauricio Manotoa Toapanta Certified Translator ID: 1710738483 (1005-14-1279868 SENESCYT) xv INTRODUCCIÓN “Uno de los primeros lenguajes de programación para el desarrollo de aplicaciones web es Perl1, el mismo que apareció antes de que internet fuera de fácil accesibilidad al público en general, posteriormente en 1995 el programador Rasmus Lerdorf puso a disposición el lenguaje PHP con lo que el desarrollo de aplicaciones web despegó. Hoy en día, incluso muchas de estas aplicaciones se han desarrollado en PHP, como Google, Facebook y Wikipedia” (BARZANALLA, 2012). En la actualidad existen un gran número de lenguajes de aplicaciones web, siendo parte de estos Ruby con su framework Rails. Bajo este lenguaje se han desarrollado aplicaciones como: Twitter en su inicio, Hulu propiedad de NBC, FOX y Disney para crear su plataforma de distribución audiovisual digital, Bloomberg una de las mayores corporaciones del mundo dedicada a las finanzas, twitch una de las plataformas de video especializada en el streaming en directo de partidas de videojuegos, entre otras aplicaciones. Ruby on Rails es un entorno de desarrollo web de código abierto que está optimizado para satisfacción de los programadores y de la productividad. Permite escribir un buen código favoreciendo la convención antes que la configuración. Además de construir aplicaciones web flexibles y robustas rápidamente. Las dos piezas principales de este entorno son dos: Ruby, es un lenguaje de programación enfocado a la simplicidad y a la productividad, con una sintaxis elegante y natural. Es un lenguaje de script totalmente orientado a objetos. La segunda pieza principal es Rails, este es un framework creado para el desarrollo de aplicaciones web, para entender mejor, es una serie de utilidades y herramientas para crear aplicaciones web más rápidamente que haciéndolo desde cero. “Ruby on Rails fue escrito por David Heinemeier Hansson a partir de su trabajo en Basecamp. Fue liberado al público por primera vez en julio de 2004” (HEINEMEIER, 2006). 1 Inventado por Larry Wall en 1987 1 Al momento de escoger una herramienta para el desarrollo de aplicaciones web se inicia por la búsqueda de material didáctico, guie el desarrollo de la aplicación y contar con un tutorial completo que permita los primeros pasos en el desarrollo de alguna aplicación en particular. Actualmente en el Ecuador no se cuenta con un documento que cumpla lo descrito en el párrafo anterior por lo que el objetivo principal es; describir analíticamente el desarrollo completo de una aplicación web, utilizando el lenguaje de programación Ruby y el framework exclusivo Rails. Las aplicaciones web brindan servicios importantes entre ellas el acceso a la información, disminuye costos, tiempos y aumenta la accesibilidad a la información desde cualquier parte del mundo. Existen varios lenguajes y herramientas para el desarrollo de aplicaciones web, tanto propietario como gratuito. Para un cierto grupo de la población que quiera inducirse en el mundo del desarrollo de aplicaciones web resulta costoso la adquisición licencias o herramientas, por lo que muy probablemente buscará variantes gratuitas a fin de satisfacer su necesidad de empezar en el desarrollo de aplicaciones. Ruby on Rails es una herramienta gratuita que permite el desarrollo de aplicaciones web orientada a objetos, se ha diseñado para que sea fácil de leer y escribir, no solo gratis sino libre para usarlo, copiarlo, modificarlo y distribuirlo. La información que existe acerca de Ruby on Rails se encuentra dispersa y no es capaz de influir en la población para el uso de esta herramienta, sabiendo además que no hay aplicaciones difundidas desarrolladas con Ruby on Rails en el Ecuador, éste estudio es el primer trabajo investigativo sobre esta tecnología que ayudará a la comunidad a acercarse más al lenguaje y tener una fuente de investigación que facilite el desarrollo de las primeras aplicaciones web. 2 1. MARCO TEÓRICO 1.1 1.1.1 Ruby on Rails Ruby Es un lenguaje de programación enfocado a la simplicidad y a la productividad, con una sintaxis elegante y natural, que le hace muy fácil de entender. Es un lenguaje de script (no compilado), totalmente orientado a objetos, aquí todo es un objeto y por tanto se le pueden asociar propiedades y métodos, así como redefinir o extender su comportamiento. Desde su liberación pública en 1995, Ruby ha atraído devotos desarrolladores de todo el mundo. En el 2006, Ruby alcanzó reconocimiento masivo, formándose grupos de usuarios activos en las ciudades más importantes del mundo y llenando las capacidades de las conferencias relacionadas a Ruby. El índice TIOBE, que mide el crecimiento de los lenguajes de programación, ubica a Ruby en la posición #13 del ranking mundial. Refiriéndose a su crecimiento, predicen, “Todo indica que Ruby llegará a estar entre los primeros”. (RUBY, s.f.) 1.1.2 Rails Ruby on Rails, también conocido como RoR o Rails, es un framework de aplicaciones web de código abierto escrito en el lenguaje de programación Ruby, siguiendo el paradigma de la arquitectura Modelo Vista Controlador (MVC). “Trata de combinar la simplicidad con la posibilidad de desarrollar aplicaciones del mundo real escribiendo menos código que con otros frameworks y con un mínimo de configuración. El lenguaje de programación Ruby permite la metaprogramación, de la cual Rails hace uso, lo que resulta en una sintaxis que muchos de sus usuarios encuentran muy legible” (WIKIPEDIA, Ruby on Rails, 2016). La filosofía Rails incluye dos grandes principios: 3 Don't Repeat Yourself: como lo dicen en (WIKIPEDIA, Ruby on Rails, 2016) se lo conoce como DRY, es un principio de desarrollo de software que establece que "Cada pieza de conocimiento debe tener una única representación autorizada, sin ambigüedades, dentro de un sistema." Para no escribir la misma información una y otra vez. Convention Over Configuration: como dice en (WIKIPEDIA, Ruby on Rails, 2016) Rails tiene opiniones sobre la mejor manera de hacer muchas cosas en una aplicación web, y los valores predeterminados para este conjunto de convenciones, antes que se requiera que se especifique cada detalle a través de archivos de configuración sin fin. 1.1.3 Ruby gem Una gema es, básicamente, la manera en que Ruby permite distribuir programas, módulos o librerías que extienden funcionalidad, casi siempre específica, volviendo más fácil el flujo de desarrollo. Por ejemplo, hay gemas para Rails que se encargan de la autenticación de usuarios como es el caso de Devise. Para casi cada acción que se realiza repetitivamente a la hora de programar, existe una gema que lo vuelve todo más fácil. Cabe decir que las gemas pueden depender de otras gemas para funcionar. Rails es la gema más famosa de Ruby, muchas personas llegan a Rails sin saber Ruby y después se dan cuenta que deben conocer la sintaxis básica del lenguaje para aprender a utilizar la herramienta (PUENTE, 2014). 1.2 Arquitectura MVC El modelo vista controlador (MVC) según (BURBECK, 2015) es un patrón de arquitectura de software que separa los datos y la lógica de negocio de una aplicación de la interfaz de usuario y el módulo encargado de gestionar los eventos y las comunicaciones. Para ello MVC propone la construcción de tres componentes distintos que son el modelo, la vista y el controlador, es decir, por un lado define componentes para la representación de la información, y por otro lado para la interacción del usuario. Este patrón de arquitectura de software se basa en las ideas de reutilización de código y la separación de conceptos. 4 1.2.1 Modelo a) Active Record Es la capa del sistema, responsable de representar los datos del negocio y la lógica. Active Record facilita la creación y el uso de objetos del negocio, cuyos datos requieren un almacenamiento en una base de datos. (DEV & Noria, s.f.) Object-Relational Mapping.- Comúnmente conocida como su abreviatura ORM, según (DEV & Noria, s.f.) es una técnica que conecta los objetos de una aplicación a las tablas en una base de datos relacional. Usando ORM, las propiedades y relaciones de los objetos en una aplicación se pueden almacenar y recuperar fácilmente de una base de datos, sin tener que escribir sentencias SQL y con menos código de acceso a la base de datos. Active Record da varios mecanismos, siendo los más importantes: Representar modelos y sus datos. Representar asociaciones entre los modelos. Representar jerarquías de herencia a través de modelos relacionados. Validar los modelos antes de que se conservan en la base de datos. Realizar operaciones de base de datos de una forma orientada a objetos. Active Record usa algunas convenciones de nombres para averiguar cómo se debe crear la correlación entre los modelos y tablas de la base de datos. Rails pluraliza sus nombres de clase para encontrar la tabla de la base de datos respectiva. Así, para una clase Book, se debe tener una tabla en la base de datos llamada books. En Rails los mecanismos de pluralización son muy potentes, siendo capaces de pluralizar (y singularizar) palabras regulares e irregulares. Al utilizar nombres de clases compuestas de dos o más palabras, el nombre de la clase modelo debe seguir las convenciones de Ruby, usando el formulario de CamelCase, mientras que el nombre de la tabla debe contener las palabras separadas por guiones. (DEV & Noria, s.f.) Hay algunos nombres de columnas opcionales que añaden funciones adicionales para casos de Active Record, según (DEV & Noria, s.f.): 5 created_at: Automáticamente se establece con la fecha y hora actuales cuando se creó por primera vez el registro. updated_at: Automáticamente se establece con la fecha y hora actuales cada vez que se actualiza el registro. lock_version: Añade un bloqueo optimistic a un modelo. type: Especifica que el modelo utiliza herencias de tablas individuales. (association_name)_type: Almacena el tipo de asociaciones polimórficas. (table_name)_count: Se utiliza para almacenar en caché el número que pertenece a los objetos sobre las asociaciones. Por ejemplo, una columna comments_count en una clase Articles que tiene muchos casos de comentarios en caché, es decir el número de comentarios existentes para cada artículo. CRUD: lectura y escritura de datos CRUD es un acrónimo de cuatro verbos que se utiliza para operar los datos: C reate, R ead, U pdate y D elete, Active Record crea automáticamente métodos para permitir a una aplicación leer y manipular los datos almacenados dentro de sus tablas. (DEV & Noria, s.f.) Create (Crear).- Los objetos de Active Record pueden ser creados a partir de un hash o un bloque. El método new devolverá un nuevo objeto, mientras que create devolverá el objeto y lo guarda en la base de datos. (DEV & Noria, s.f.) Por ejemplo, dado un modelo de User con atributos de name y occupation, llamando al método create va a crear y guardar un nuevo registró en la base de datos: user = User.create(name: "David", occupation: "Code Artist") Read (Leer).- Active Record proporciona una API para acceder a datos dentro de una base de datos. A continuación se presentan algunos ejemplos de diferentes métodos de acceso a datos, proporcionados por Active Record. # Devuelve una colección con todos los usuarios users = User.all # Devolver el primer usuario user = User.first # Devuelve el primer usuario llamado David 6 david = User.find_by(name: 'David') # Encuentra todos los usuarios llamados David, que son Code Artists y ordenar por created_at en orden cronológico inverso users = User.where(name: 'David', occupation: 'Code Artist').order(created_at: :desc) Update (actualizar).- Una vez que un objeto Active Record se ha recuperado, sus atributos pueden ser modificados y que se pueden guardar en la base de datos. user = User.find_by(name: 'David') user.name = 'Dave' user.save La abreviatura de esto es utilizar una asignación de nombres de atributos de hash al valor deseado, así: user = User.find_by(name: 'David') user.update(name: 'Dave') Esto es muy útil cuando se actualiza varios atributos a la vez. Si, por el contrario, desea actualizar varios registros, se puede usar el método de la clase update_all así: User.update_all "max_login_attempts = 3, must_change_password = 'true'" Delete (Borrar).- Así mismo, una vez recuperado un objeto de Active Record puede ser destruido, el cual se elimina de la base de datos. user = User.find_by(name: 'David') user.destroy b) Migraciones Las migraciones son una forma conveniente para modificar el esquema de la base de datos a través del tiempo, de una manera consistente y fácil. Estos utilizan una conexión DSL de Ruby de modo que no tiene que escribir SQL a mano, permitiendo que su esquema y los cambios de la base de datos sean independientes. (DEV & Noria, s.f.) Se puede pensar en cada migración como una nueva versión de la base de datos. Un esquema comienza vacío, y cada migración lo modifica para agregar o quitar tablas, 7 columnas o entradas. Active Record sabe cómo actualizar el esquema a lo largo de esta línea de tiempo, llevándolo desde cualquier punto que esté hacia la última versión. Active Record también actualizará su archivo db/schema.rb para que coincida con la estructura hasta la fecha de la base de datos. Los generadores de modelo y generadores de Scaffold crearán migraciones apropiadas para la creación de un nuevo modelo. Esta migración ya contendrá instrucciones para la creación de la tabla correspondiente. Si se le dice a Rails las columnas que se desea, entonces las declaraciones de estas columnas también se crean. Las migraciones se almacenan como archivos en el directorio db/migrate, uno para cada clase de migración. El nombre del archivo es de la forma YYYYMMDDHHMMSS_create_products.rb. class CreateProducts < ActiveRecord::Migration def change create_table :products do |t| t.string :name t.text :description t.timestamps null: false end end end Esta migración crea un método llamado change que se usará cuando se ejecuta esta migración. La acción que define este método también es reversible, lo que significa que Rails sabe cómo revertir el cambio realizado por esta migración, en caso de que se desee invertir en cualquier momento. Además agrega una tabla denominada products con una columna name y una columna description. Una columna con la clave principal de nombre ID también se añadirá de forma implícita, ya que es la clave principal por defecto para todos los modelos Active Record. El macro timestamps añade dos columnas, created_at y updated_at. Estas columnas especiales son administradas automáticamente por Active Record. (DEV & Noria, s.f.) 8 c) Validaciones Las validaciones se utilizan para garantizar que sólo los datos válidos se guarden en la base de datos. Por ejemplo, puede ser importante para su aplicación asegurarse de que cada usuario proporciona una dirección de correo electrónico válida. Las validaciones a nivel de modelo son la mejor manera de garantizar que sólo los datos válidos se guarden en la base de datos. Rails hace que sean fáciles de usar, y también le permite crear sus propios métodos de validación. (DEV & Noria, s.f.) ¿Cuándo ocurren las validaciones? Hay dos tipos de objetos de Active Record: las que corresponden a una fila dentro de su base de datos y aquellos que no lo hacen. Cuando se crea un objeto nuevo, por ejemplo, utilizando el método new, ese objeto no pertenece a la base de datos todavía. Una vez que llame al método save, el objeto se guardará en la tabla de la base de datos adecuada. Active Record utiliza el método de instancia new_record para determinar si un objeto ya está en la base de datos o no lo está. Considere la siguiente clase Active Record simple: class Person < ActiveRecord::Base end Se puede ver cómo funciona examinado algunos salidas de la consola de rails: $ bin/rails console >> p = Person.new(name: "John Doe") => #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil> >> p.new_record? => true >> p.save => true >> p.new_record? => false La creación y almacenamiento de un nuevo registro enviará una operación de SQL INSERT a la base de datos. La actualización de un registro existente enviará una operación de SQL UPDATE. Las validaciones se suelen ejecutar antes de que estos comandos se envíen a la base de datos. Si algunas validaciones fallan, el objeto se marca como no válido y Active Record no llevará a cabo la operación INSERT o UPDATE. Esto 9 evita el almacenamiento de un objeto no válido en la base de datos. Se puede optar por tener validaciones específicas que se ejecutan cuando se crea un objeto, guarda o actualiza. Los siguientes métodos desencadenan validaciones, y se guardará el objeto en la base de datos sólo si el objeto es válido: create, create!, save, save!, update, update! Tenga en cuenta que guardar también tiene la capacidad de saltar validaciones si se aprueba validate:false como argumento. Esta técnica se debe utilizar con precaución. save(validate: false) Métodos de validación Active Record ofrece muchos métodos de validación predefinidas que se pueden utilizar directamente dentro de sus definiciones de clase. Estos métodos proporcionan reglas de validación comunes. Cada vez que falla la validación, se añade un mensaje de error a la colección de errores de los objetos, y este mensaje está asociado con el atributo a ser validado. Cada método acepta un número arbitrario de nombres de atributos, así que con una sola línea de código se puede agregar el mismo tipo de validación a varios atributos. Acceptance.- Este método valida que una casilla de verificación (checkbox) en la interfaz de usuario se ha aceptado cuando se envía un formulario. Esto se usa típicamente cuando el usuario tiene que estar de acuerdo con los términos de su aplicación de servicio, confirme la lectura de un texto, o cualquier concepto similar. Esta validación es muy específica para aplicaciones web y esta "validación" no tiene que ser guardada en su base de datos (si no tiene un campo para ello, este método crea un atributo virtual). class Person < ActiveRecord::Base validates :terms_of_service, acceptance: true end 10 Validates_associated.- Debe utilizar este método cuando el modelo tiene asociaciones con otros modelos y que también necesita ser validado. Cuando intenta guardar su objeto, valid? será llamado a cada uno de los objetos asociados. class Library < ActiveRecord::Base has_many :books validates_associated :books end Confirmation.- Debe utilizar este método cuando se tiene dos campos de texto que deben tener exactamente el mismo contenido. Por ejemplo, es posible que desee confirmar una dirección de correo electrónico o una contraseña. Esta validación crea un atributo virtual cuyo nombre es el nombre del campo que tiene que ser confirmado con "_confirmation" adjuntado. class Person < ActiveRecord::Base validates :email, confirmation: true end En su plantilla de vista se podría utilizar algo como: <%= text_field :person, :email %> <%= text_field :person, :email_confirmation %> Exclusion.- Este método valida que los valores de los atributos no estén incluidos en un conjunto dado. De hecho, este conjunto puede ser cualquier objeto numerable. class Account < ActiveRecord::Base validates :subdomain, exclusion: { in: %w(www us ca jp), message: "%{value} is reserved." } end Format.- Este método valida los valores de los atributos probando si coinciden con una expresión regular dada, que se especifica mediante la opción :with. class Product < ActiveRecord::Base validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/, message: "only allows letters" } end Alternativamente, se puede requerir que el atributo especificado no coincida con la expresión regular mediante el uso de la opción :without. 11 Inclusion.- Este método valida que los valores de los atributos se incluyen en un conjunto dado. De hecho, este conjunto puede ser cualquier objeto numerable. class Coffee < ActiveRecord::Base validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" } end Length.- Este método valida la longitud de los valores de los atributos. Se ofrece una variedad de opciones, por lo que puede especificar restricciones de longitud de diferentes maneras: class Person < ActiveRecord::Base validates :name, length: { minimum: 2 } validates :bio, length: { maximum: 500 } validates :password, length: { in: 6..20 } validates :registration_number, length: { is: 6 } end Las posibles opciones de length de restricción son: :minimum - El atributo no puede tener menos de la longitud especificada. :maximum - El atributo no puede tener más de la longitud especificada. :in (o :within) - La longitud del atributo debe ser incluido en un intervalo dado. El valor de esta opción debe tener un intervalo. :is - La longitud del atributo debe ser igual al valor dado. Numericality.- Este método valida que sus atributos sólo tienen valores numéricos. De forma predeterminada, se separa con un signo opcional seguido de un número de coma flotante o integral. Para especificar que sólo permite los números enteros se puede especificar :only_integer como verdadero. Si establece :only_integer como verdadero , entonces se usará la expresión regular /\A[+-]?\d+\Z/ para validar el valor del atributo. De lo contrario, tratará de convertir el valor a un número Float. class Player < ActiveRecord::Base validates :points, numericality: true validates :games_played, numericality: { only_integer: true } end 12 Además :only_integer , también acepta las siguientes opciones para añadir restricciones a los valores aceptados: :greater_than - Especifica que el valor debe ser mayor que el valor suministrado. :greater_than_or_equal_to - Especifica que el valor debe ser mayor o igual que el valor suministrado. :equal_to - Especifica que el valor debe ser igual al valor suministrado. :less_than - Especifica que el valor debe ser menor que el valor suministrado. :less_than_or_equal_to - Especifica que el valor debe ser menor o igual al valor suministrado. :odd - Especifica que el valor debe ser un número impar :even - Especifica que el valor debe ser un número par si es true. Presence.- Este método valida que los atributos especificados no están vacíos. Se utiliza el método blank? para comprobar si el valor es nulo o una cadena en blanco, es decir, una cadena que está vacía o consiste en un espacio en blanco. class Person < ActiveRecord::Base validates :name, :login, :email, presence: true end Absence.- Este método valida que los atributos especificados estén ausentes. Utiliza el método present? para comprobar si el valor no es nulo o bien una cadena en blanco, es decir, una cadena que está vacía o consiste en un espacio en blanco. class Person < ActiveRecord::Base validates :name, :login, :email, absence: true end Uniqueness.- Este método valida que el valor del atributo sea único, justo antes de que el objeto se guarde. No crea una restricción de unicidad en la base de datos, por lo que puede suceder que dos conexiones de bases de datos diferentes crean dos registros con el mismo valor para una columna que tiene la intención de ser único. Para evitar esto, se debe crear un índice único en ambas columnas en su base de datos. 13 class Account < ActiveRecord::Base validates :email, uniqueness: true end La validación ocurre mediante la realización de una consulta SQL en la tabla del modelo, la búsqueda de un registro existente con el mismo valor en ese atributo. Opciones de validación comunes :allow_nil.- La opcion :allow_nil se salta la validación cuando el valor que se está validando es nula o vacía. class Coffee < ActiveRecord::Base validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" }, allow_nil: true end :allow_blank.- La opción :allow_blank es similar a la opción :allow_nil . Esta opción le permitirá pasar la validación si el valor del atributo es blank?, como nulo o una cadena vacía, por ejemplo: class Topic < ActiveRecord::Base validates :title, length: { is: 5 }, allow_blank: true end Topic.create(title: "").valid? # => true Topic.create(title: nil).valid? # => true :message.- Como ya se ha visto, la opción :message le permite especificar el mensaje que se añadirá a la colección de errores cuando la validación falla. Cuando no se utiliza esta opción, Active Record utilizará el mensaje de error predeterminado respectivo para cada método de validación. :on.- La opción :on le permite especificar cuando la validación debería suceder. El comportamiento por defecto para todos los métodos de validación incorporadas se va a ejecutar cuando se guarde (cuando se va a crear un nuevo registro y cuando se está actualizando). Si desea cambiarlo, puede utilizar on: :create para ejecutar la validación sólo cuando se crea un nuevo registro o on: :update para ejecutar la validación sólo cuando se actualiza un registro. 14 class Person < ActiveRecord::Base validates :email, uniqueness: true, on: :create validates :age, numericality: true, on: :update validates :name, presence: true end d) Callback Durante el funcionamiento normal de una aplicación Rails, los objetos se pueden crear, actualizar, y destruir. Active Record ofrece hooks en este ciclo de vida del objeto para que pueda controlar su aplicación y sus datos. Callbacks le permiten desencadenar la lógica antes o después de una alteración del estado de un objeto. (DEV & Noria, s.f.) Visión general sobre Callbacks.- Callbacks son métodos que serán llamadas en ciertos momentos del ciclo de vida de un objeto. Con los Callback es posible escribir código que se ejecutará cada vez que se crea un objeto Active Record, crear, actualizar, eliminar, validar, o cargar desde la base de datos. Registro Callback.- Para utilizar los Callbacks disponibles, es necesario registrarlos. Puede implementar los Callbacks como métodos ordinarios y utilizar un método de clase de estilo macro para registrarlos como Callback: class User < ActiveRecord::Base validates :login, :email, presence: true before_validation :ensure_login_has_a_value protected def ensure_login_has_a_value if login.nil? self.login = email unless email.blank? end end end Los Callbacks también pueden ser registrados sólo en determinados eventos del ciclo de vida: class User < ActiveRecord::Base before_validation :normalize_name, on: :create after_validation :set_location, on: [ :create, :update ] protected 15 def normalize_name self.name = self.name.downcase.titleize end def set_location self.location = LocationService.query(self) end end Se considera una buena práctica para declarar métodos de Callbacks como protegido o privado. Si se deja pública, se pueden llamar desde fuera del modelo y violan el principio de encapsulación del objeto. Clases de callback A veces los métodos de callback que se escribe son lo suficientemente útiles para ser reutilizados por otros modelos. Active Record hace posible la creación de clases que encapsulan los métodos de callback, lo que hace muy fácil de volver a utilizarlos. He aquí un ejemplo en el que se crea una clase con un callback after_destroy para un modelo PictureFile : class PictureFileCallbacks def after_destroy(picture_file) if File.exist?(picture_file.filepath) File.delete(picture_file.filepath) end end end Cuando se declara dentro de una clase, los métodos de Callbacks recibirán el modelo de objeto como parámetro. Ahora puede utilizar la clase Callbacks en el modelo: class PictureFile < ActiveRecord::Base after_destroy PictureFileCallbacks.new end Tenga en cuenta que se tiene que crear una instancia de un nuevo objeto PictureFileCallbacks, ya que la Callbacks está declarada como un método de instancia. Esto es particularmente útil si los Callbacks hacen uso del estado del objeto instanciado. A menudo, sin embargo, tendrá más sentido declarar los Callbacks como métodos de clase: 16 class PictureFileCallbacks def self.after_destroy(picture_file) if File.exist?(picture_file.filepath) File.delete(picture_file.filepath) end end end Si el método de Callbacks se declara de esta manera, no será necesario crear una instancia de objeto PictureFileCallbacks . class PictureFile < ActiveRecord::Base after_destroy PictureFileCallbacks end Se puede declarar muchos Callbacks que desee dentro de las clases Callbacks. e) Asociaciones ¿Por qué se necesita las asociaciones entre los modelos?, (DEV & Noria, s.f.) afirman, que hacen las operaciones comunes más simples y fácil en el código. Por ejemplo, considere una aplicación de simples guías que forman un modelo para los clientes y un modelo para los pedidos. Cada cliente puede tener muchos pedidos. Sin asociaciones, las declaraciones modelo se vería así: class Customer < ActiveRecord::Base end class Order < ActiveRecord::Base end Ahora, suponga que desee añadir un nuevo pedido de un cliente existente. Entonces de debe hacer algo como esto: @order = Order.create(order_date: Time.now, customer_id: @customer.id) O considerar la eliminación de un cliente, y asegurar que todas sus órdenes se eliminan: @orders = Order.where(customer_id: @customer.id) @orders.each do |order| order.destroy end @customer.destroy 17 Con las asociaciones de Active Record, puede simplificar esto y otras operaciones diciéndole de forma declarativa a Rails que hay una conexión entre los dos modelos. Aquí está el código para la creación de clientes y pedidos: class Customer < ActiveRecord::Base has_many :orders, dependent: :destroy end class Order < ActiveRecord::Base belongs_to :customer end Con este cambio, la creación de un nuevo pedido para un cliente en particular es fácil: @order = @customer.orders.create(order_date: Time.now) La eliminación de un cliente y todos sus pedidos es mucho más fácil: @customer.destroy. Tipos de Asociaciones En Rails, una asociación es una conexión entre dos modelos de Active Record. Las asociaciones se implementan mediante llamadas de tipo macro, para que de forma declarativa pueda agregar características a sus modelos. Por ejemplo, al declarar en un modelo belongs_to a otro modelo, se indica a Rails que mantenga la información de la clave primaria Key-Foreign entre instancias de los dos modelos, y también se obtiene una serie de métodos de utilidad añadido a su modelo. Rails admite seis tipos de asociaciones: belongs_to, has_one, has_many, has_many :through, has_one :through, has_and_belongs_to_many. Asociación belongs_to.- Una asociación belongs_to establece una conexión uno-auno con otro modelo, de manera que cada instancia del modelo que se declara "pertenece a" una instancia del otro modelo. Por ejemplo, si su aplicación incluye clientes y pedidos, y cada orden puede ser asignado a un solo cliente, se declara el modelo de esta manera: class Order < ActiveRecord::Base belongs_to :customer end 18 Figura 1. Asociación belong_to (DEV & Noria, s.f.) Asociación has_one.- Una asociación has_one también establece una conexión uno-auno con otro modelo, pero con una semántica diferente (y consecuencias). Esta asociación indica que cada instancia de un modelo contiene o posee una instancia de otro modelo. Por ejemplo, si cada proveedor en su aplicación tiene sólo una cuenta, se puede declarar el modelo de proveedor de la siguiente manera: class Supplier < ActiveRecord::Base has_one :account end Figura 2. Asociación has_one (DEV & Noria, s.f.) Asociación has_many.- Una asociación has_many indica una conexión de uno a muchos con otro modelo. A menudo encontrará esta asociación y en el "otro lado" una asociación belongs_to. Esta asociación indica que cada instancia del modelo tiene cero o más instancias de otro modelo. Por ejemplo, en una aplicación que contiene clientes y pedidos, el modelo cliente podría ser declarado como esto: class Customer < ActiveRecord::Base has_many :orders end 19 El nombre del otro modelo está en plural cuando se declara una asociación has_many. Figura 3. Asociación has_many (DEV & Noria, s.f.) Asociación has_many :through.- Una asociación has_many:through es a menudo utilizada para establecer una conexión de muchos a muchos con otro modelo. Esta asociación indica que el modelo que se declara se puede emparejar con cero o más instancias de otro modelo procediendo a través de un tercer modelo. Por ejemplo, considere una práctica médica que los pacientes hacen citas para ver a los médicos. Las declaraciones de asociación pertinentes podrían tener este aspecto: class Physician < ActiveRecord::Base has_many :appointments has_many :patients, through: :appointments end class Appointment < ActiveRecord::Base belongs_to :physician belongs_to :patient end class Patient < ActiveRecord::Base has_many :appointments has_many :physicians, through: :appointments end Figura 4. Asociación has_many :through (DEV & Noria, s.f.) 20 Asociación has_one :through.- Una asociación has_one:through establece una conexión uno-a-uno con otro modelo. Esta asociación indica que el modelo que se declara se puede emparejar con una instancia de otro modelo procediendo a través de un tercer modelo. Por ejemplo, si cada proveedor tiene una cuenta, y cada cuenta se asocia con una historia de la cuenta, entonces el modelo de proveedor podría tener este aspecto: class Supplier < ActiveRecord::Base has_one :account has_one :account_history, through: :account end class Account < ActiveRecord::Base belongs_to :supplier has_one :account_history end class AccountHistory < ActiveRecord::Base belongs_to :account end Figura 5. Asociación has_one :through (DEV & Noria, s.f.) Asociación has_and_belongs_to_many.- crea una conexión directa de muchos a muchos con otro modelo, con ningún modelo intermedio. Por ejemplo, si su aplicación incluye conjuntos y partes, con cada conjunto que tiene muchas partes y cada parte apareciendo en muchos conjuntos, se podría declarar los modelos de esta manera: class Assembly < ActiveRecord::Base has_and_belongs_to_many :parts end 21 class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end Figura 6. Asociación has_and_belongs_to_many (DEV & Noria, s.f.) Las asociaciones polimórficas Un giro un poco más avanzado en las asociaciones es la asociación polimórfica. Con las asociaciones polimórficas, un modelo puede pertenecer a más de un modelo, en una sola asociación. Por ejemplo, es posible que tenga un modelo de imagen que pertenece tanto a un modelo empleado o un modelo de producto. Así es como este podría ser declarado: class Picture < ActiveRecord::Base belongs_to :imageable, polymorphic: true end class Employee < ActiveRecord::Base has_many :pictures, as: :imageable end class Product < ActiveRecord::Base has_many :pictures, as: :imageable end Se puede pensar en una declaración polimórfica belongs_to como la creación de una interfaz que cualquier otro modelo puede utilizar. A partir de una instancia del modelo Empleado, puede recuperar una colección de imágenes: @employee.pictures, del mismo modo, se puede recuperar @product.pictures . 22 Si se tiene una instancia del modelo imagen, se puede llegar a través de su matriz @picture.imageable. Para que esto funcione, tiene que declarar tanto una columna de clave externa y una columna del tipo en el modelo que declara la interfaz polimórfica: class CreatePictures < ActiveRecord::Migration def change create_table :pictures do |t| t.string :name t.integer :imageable_id t.string :imageable_type t.timestamps null: false end add_index :pictures, :imageable_id end end Esta migración puede ser simplificada mediante el uso de la forma t.references: class CreatePictures < ActiveRecord::Migration def change create_table :pictures do |t| t.string :name t.references :imageable, polymorphic: true, index: true t.timestamps null: false end end end Figura 7. Asociaciones polimórficas (DEV & Noria, s.f.) 23 1.2.2 View (Vistas) Los Action View y Action Controller son los dos componentes principales de Action Pack. En Rails, las peticiones web son manejadas por Action Pack, que divide el trabajo, en una parte del controlador (la realización de la lógica) y una parte de la vista (presentación de una plantilla (template)). Por lo general, Action Controller se ocupará de la comunicación con la base de datos y la realización de acciones CRUD cuando sea necesario. Action View es entonces el responsable de la compilación de la respuesta. (DEV & Noria, s.f.) Las plantillas Action View se escriben utilizando Ruby embebido en etiquetas que se mezclan con HTML. Para evitar llenar las plantillas con código repetitivo, una serie de clases de métodos proporcionan un comportamiento común para las formas, las fechas y las cadenas. También es fácil de añadir nuevos métodos para su aplicación a medida que evoluciona. Uso de Action View con Rails Para cada controlador hay un directorio asociado en el directorio app/views que contiene los archivos de plantillas que componen las vistas asociadas a ese controlador. Estos archivos se utilizan para mostrar la vista del resultado de cada acción del controlador. Se va a echar un vistazo a lo que Rails hace por defecto cuando se crea un nuevo recurso utilizando el generador scaffold : $ bin/rails generate scaffold article [...] invoke scaffold_controller create app/controllers/articles_controller.rb invoke erb create app/views/articles create app/views/articles/index.html.erb create app/views/articles/edit.html.erb create app/views/articles/show.html.erb create app/views/articles/new.html.erb create app/views/articles/_form.html.erb [...] Hay una convención de nomenclatura para las vistas en Rails. Por lo general, los puntos de vista comparten su nombre con la acción del controlador asociado, como se puede ver 24 arriba. Por ejemplo, la acción index del controlador articles_controller.rb utilizará el archivo de vista index.html.erb en el directorio app/views/articles. El HTML completo que devuelve al cliente se compone de una combinación de este archivo ERB, una plantilla de diseño (Layouts Templates) que lo envuelve, y todos los parciales (Partials) que la vista puede hacer referencia. Templates, Partials and Layouts Como se ha mencionado, la salida HTML final es una composición de tres elementos Rails: Templates, Partials y Layouts (plantillas, Parciales y Diseños). A continuación se muestra una breve descripción de cada uno de ellos. Plantillas (Templates) Action View templates se pueden escribir de varias maneras. Si el archivo de plantillas tiene una extensión .erb entonces se utiliza una mezcla de ERB (Ruby Embebido) y HTML. Si el archivo de plantilla tiene una extensión .builder entonces se utiliza la biblioteca Builder::XmlMarkup. Rails soporta múltiples sistemas de plantillas y utiliza una extensión de archivo para distinguirlos entre ellos. Por ejemplo, un archivo HTML que usa el sistema de plantillas ERB tendrá .html.erb como extensión de archivo. (DEV & Noria, s.f.) ERB.- Dentro de una plantilla de ERB, el código Ruby se puede incluir con el uso de las etiquetas <% %> y <%= %>. Las etiquetas <% %> se utilizan para ejecutar código Ruby que no devuelve nada, como las condiciones, bucles o bloques, y las etiquetas <%= %> se utilizan cuando se requiere salida. Considere el siguiente bucle de nombres: <h1>Names of all the people</h1> <% @people.each do |person| %> Name: <%= person.name %><br> <% end %> El bucle se configura usando las etiquetas de incrustación regulares ( <% %> ) y se inserta el nombre utilizando la salida de la incorporación de etiquetas ( <%= %> ). Tenga en cuenta que esto no es sólo una sugerencia de uso: funciones de salida 25 regulares, tales como print y puts no se consideran en la vista con las plantillas ERB. Por lo que esto sería un error: <%# WRONG %> Hi, Mr. <% puts "Frodo" %> Para suprimir espacios en blanco iniciales y finales, puede utilizar <%- - %> intercambiable con <% y %>. Constructor (Builder).- Plantillas del Constructor son una alternativa más programática de ERB. Son especialmente útiles para la generación de contenido XML. Un objeto XmlMarkup llamado xml se realiza automáticamente a disposición de plantillas con una extensión .builder . Estos son algunos ejemplos básicos: xml.em("emphasized") xml.em { xml.b("emph & bold") } xml.a("A Link", "href" => "http://rubyonrails.org") xml.target("name" => "compile", "option" => "fast") lo que produciría: <em>emphasized</em> <em><b>emph &amp; bold</b></em> <a href="http://rubyonrails.org">A link</a> <target option="fast" name="compile" /> Cualquier método con un bloque será tratado como una etiqueta de marcado XML con el formato anidado en el bloque. Por ejemplo, los siguientes: xml.div { xml.h1(@person.name) xml.p(@person.bio) } Produciría algo así como: <div> <h1>David Heinemeier Hansson</h1> <p>A product of Danish Design during the Winter of '79...</p> </div> Parciales (Partials).- Plantillas partials - por lo general sólo llamadas " partials ". Con partials, se puede extraer piezas de código de sus plantillas a archivos separados y también volver a utilizarlos en sus plantillas. 26 Nomenclatura de partials.- Para poner un partials como parte de una vista, se utiliza el método render dentro de la vista: <%= render "menu" %> Esto hará que un archivo llamado _menu.html.erb en ese momento se presente dentro de la vista. Tenga en cuenta el caracter de guion bajo que lleva, los partials se nombran con un guion bajo inicial para distinguirlos de puntos de vista regulares, a pesar de que se hace referencia sin el guion bajo. Esto es cierto incluso cuando se está cogiendo un parcial de otra carpeta: <%= render "shared/menu" %> Ese código coge el parcial de app/views/shared/_menu.html.erb. Uso de Parciales para simplificar Vistas.- Una forma de utilizar los parciales es tratarlos como el equivalente de subrutinas; una manera de mover datos de una vista para que pueda entender lo que está pasando con mayor facilidad. Por ejemplo, es posible tener una vista similar a la siguiente: <%= render "shared/ad_banner" %> <h1>Products</h1> <p>Here are a few of our fine products:</p> <% @products.each do |product| %> <%= render partial: "product", locals: {product: product} %> <% end %> <%= render "shared/footer" %> Aquí, los parciales _ad_banner.html.erb y _footer.html.erb podrían incluir contenido que se comparte entre muchas páginas en su aplicación. No es necesario ver los detalles de estas secciones cuando se está concentrando en una página determinada. Las opciones as and object .- Por defecto ActionView::Partials::PartialRenderer tiene object en una variable local con el mismo nombre que la plantilla. Por lo tanto, dado: <%= render partial: "product" %> en product se obtiene @product en la variable local product, como si se escribiera: <%= render partial: "product", locals: {product: @product} %> 27 Con la opción as se puede especificar un nombre diferente para la variable local. Por ejemplo, si se quisiera que fuera item en lugar de product se haría: <%= render partial: "product", as: "item" %> La opción object se puede utilizar para especificar directamente el objeto que se representa en el parcial; útil cuando el objeto de la plantilla está en otra parte (por ejemplo. en una variable de instancia diferente o en una variable local). Por ejemplo, en lugar de: <%= render partial: "product", locals: {product: @item} %> se haría: <%= render partial: "product", object: @item %> las opciones as y object también se pueden utilizar juntos: <%= render partial: "product", object: @item, as: "item" %> Layouts Los diseños pueden ser utilizados para poner una plantilla de vista común en torno a los resultados de las acciones del controlador Rails. “Por lo general, una aplicación Rails tendrá un par de diseños de las páginas se presten. Por ejemplo, un sitio puede tener un diseño para un usuario conectado y otro para la parte del sitio de marketing o ventas. El diseño de inicio de sesión de usuario podría incluir la navegación a nivel superior que debe estar presente en muchas acciones del controlador. La disposición de ventas para una aplicación SaaS podría incluir la navegación de nivel superior para cosas como precios y las páginas de Contacto” (DEV & Noria, s.f.). Auxiliares para la generación de elementos de un formulario Rails ofrece una serie de ayudas para la generación de elementos de un formulario, como checkboxes, text fields, y radio buttons. Estos ayudantes básicos, con nombres que terminan en funciones acabadas en _tag (como text_field_tag y check_box_tag ), generan un solo elemento <input> . El primer parámetro a estos siempre es el nombre de la 28 entrada. Cuando se envía el formulario, el nombre será pasado junto con los datos del formulario, y hará su camino al params hash en el controlador con el valor introducido por el usuario para ese campo. Al asignar nombres a las entradas, Rails utiliza ciertas convenciones que hacen posible que presente los parámetros con los valores no escalares tales como matrices o hashes, que también serán accesibles en params. Las casillas de verificación (checkboxes).- Las casillas de verificación son los controles del formulario que le dan al usuario un conjunto de opciones que se pueden activar o desactivar: <%= <%= <%= <%= check_box_tag(:pet_dog) %> label_tag(:pet_dog, "I own a dog") %> check_box_tag(:pet_cat) %> label_tag(:pet_cat, "I own a cat") %> Esto genera lo siguiente: <input <label <input <label id="pet_dog" name="pet_dog" type="checkbox" value="1" /> for="pet_dog">I own a dog</label> id="pet_cat" name="pet_cat" type="checkbox" value="1" /> for="pet_cat">I own a cat</label> El primer parámetro a check_box_tag , por supuesto, es el nombre de la entrada. El segundo parámetro, naturalmente, es el valor de la entrada. Este valor se incluye en los datos del formulario cuando se marca la casilla de verificación. Los botones de opción (Radio Buttons).- Los radio buttons, es similar a las casillas de verificación, son los controles que especifican un conjunto de opciones en el que se excluyen entre sí (es decir, el usuario sólo puede escoger uno): <%= radio_button_tag(:age, "child") %> <%= label_tag(:age_child, "I am younger than 21") %> <%= radio_button_tag(:age, "adult") %> <%= label_tag(:age_adult, "I'm over 21") %> Salida: <input id="age_child" name="age" type="radio" value="child" /> <label for="age_child">I am younger than 21</label> <input id="age_adult" name="age" type="radio" value="adult" /> <label for="age_adult">I'm over 21</label> 29 Al igual que con check_box_tag , el segundo parámetro para radio_button_tag es el valor de la entrada. Debido a que estos dos botones de radio comparten el mismo nombre ( la edad ), el usuario sólo será capaz de seleccionar uno de ellos, y params[: edad] contendrá ya sea "niño" o "adulto" Otros métodos de interés Otros controles del formulario que vale la pena mencionar son: áreas de texto, campos de contraseña, campos ocultos, campos de búsqueda, campos de teléfono, los campos de fecha, campos de hora, campos de color, los campos de fecha y hora, los campos de fecha y hora local, campos de mes, los campos de la semana, los campos de URL, campos de correo electrónico, número campos y campos de intervalo: <%= <%= <%= <%= <%= <%= <%= <%= <%= <%= <%= <%= <%= <%= <%= <%= text_area_tag(:message, "Hi, nice site", size: "24x6") %> password_field_tag(:password) %> hidden_field_tag(:parent_id, "5") %> search_field(:user, :name) %> telephone_field(:user, :phone) %> date_field(:user, :born_on) %> datetime_field(:user, :meeting_time) %> datetime_local_field(:user, :graduation_day) %> month_field(:user, :birthday_month) %> week_field(:user, :birthday_week) %> url_field(:user, :homepage) %> email_field(:user, :address) %> color_field(:user, :favorite_color) %> time_field(:task, :started_at) %> number_field(:product, :price, in: 1.0..20.0, step: 0.5) %> range_field(:product, :discount, in: 1..100) %> Salida: <textarea id="message" name="message" cols="24" rows="6">Hi, nice site</textarea> <input id="password" name="password" type="password" /> <input id="parent_id" name="parent_id" type="hidden" value="5" /> <input id="user_name" name="user[name]" type="search" /> <input id="user_phone" name="user[phone]" type="tel" /> <input id="user_born_on" name="user[born_on]" type="date" /> <input id="user_meeting_time" name="user[meeting_time]" type="datetime" /> <input id="user_graduation_day" name="user[graduation_day]" type="datetime-local" /> <input id="user_birthday_month" name="user[birthday_month]" type="month" /> 30 <input id="user_birthday_week" name="user[birthday_week]" type="week" /> <input id="user_homepage" name="user[homepage]" type="url" /> <input id="user_address" name="user[address]" type="email" /> <input id="user_favorite_color" name="user[favorite_color]" type="color" value="#000000" /> <input id="task_started_at" name="task[started_at]" type="time" /> <input id="product_price" max="20.0" min="1.0" name="product[price]" step="0.5" type="number" /> <input id="product_discount" max="100" min="1" name="product[discount]" type="range" /> 1.2.3 Controlador (Controller) Action Controller es la C en MVC. Después que el enrutamiento ha determinado qué controlador se debe utilizar para una petición, el controlador se encarga de que tome sentido la petición y producir la salida adecuada. Por suerte, Action Controller hace la mayor parte de las bases y usa convenciones inteligentes para hacer esto lo más sencillo posible. Para la mayoría de las aplicaciones convencionales RESTful, el controlador recibirá la solicitud (esto es invisible para el usuario como para el desarrollador), busca o guarda los datos de un modelo y los utiliza a fin de crear una salida HTML. Si el controlador tiene que hacer las cosas un poco diferentes, eso no es un problema, esto es sólo la forma más común para que un controlador funcione. (DEV & Noria, s.f.) Un controlador de este modo puede ser pensado como un intermediario entre los modelos y las vistas. Esto hace que los datos del modelo estén disponibles a la vista para que pueda mostrar al usuario, y se guarda o actualizan los datos del usuario para el modelo. Controlador de convención de nomenclatura La convención de nomenclatura de los controladores en Rails favorece la pluralización de la última palabra en el nombre del controlador, aunque no es estrictamente necesario (ApplicationController ). Por ejemplo, ClientsController es preferible a ClientController, SiteAdminsController es preferible a SiteAdminController o SitesAdminsController , y así sucesivamente. 31 Siguiendo esta convención se permitirá utilizar los generadores de ruta por defecto sin necesidad de calificar cada uno :path o :controller, y mantiene el uso de URL y ruta de ayuda de uso constante a través de la aplicación. La convención de nomenclatura controlador difiere de la convención de nombres de modelos, que se espera que sea nombrado en forma singular. Métodos y acciones Un controlador es una clase que hereda de Ruby ApplicationController y tiene métodos al igual que cualquier otra clase. Cuando la aplicación recibe una petición, el enrutamiento determinará qué controlador y la acción a ejecutar, a continuación, Rails crea una instancia de ese controlador y corre el método con el mismo nombre que la acción. (DEV & Noria, s.f.) class ClientsController < ApplicationController def new end end A modo de ejemplo, si un usuario va a la /clients/new en su aplicación para añadir un nuevo cliente, Rails creará una instancia de ClientsController y ejecuta el método new. Tenga en cuenta que el método vacío del ejemplo anterior podría funcionar muy bien porque Rails por defecto crea la vistal new.html.erb a menos que la acción diga lo contrario. El método new podría facilitar la vista de una variable de instancia @client mediante la creación de un nuevo cliente: def new @client = Client.new end ApplicationController hereda de ActionController::Base, que define una serie de métodos útiles. Sólo los métodos públicos son invocables como acciones. Es una mejor práctica para reducir la visibilidad de los métodos que no están destinados a ser acciones, como métodos o filtros auxiliares. 32 Parámetros Es probable que desee acceder a los datos enviados por el usuario u otros parámetros en sus acciones del controlador. Hay dos tipos de parámetros posibles en una aplicación web. La primera son los parámetros que se envían como parte de la URL, llamados parámetros de cadena de consulta. La cadena de consulta es todo lo que se encuentra después de "?" en la URL. El segundo tipo de parámetro se refiere generalmente como datos POST. Esta información generalmente proviene de un formulario HTML que ha sido rellenado por el usuario. Se llama datos POST, ya que sólo puede ser enviado como parte de una petición HTTP POST. Rails no hace ninguna distinción entre los parámetros de cadena de consulta y parámetros POST, y ambos están disponibles en params hash en su controlador: class ClientsController < ApplicationController def index if params[:status] == "activated" @clients = Client.activated else @clients = Client.inactivated end end def create @client = Client.new(params[:client]) if @client.save redirect_to @client else render "new" end end end Sesión Su aplicación tiene una sesión para cada usuario en la que puede almacenar pequeñas cantidades de datos que se mantiene entre las solicitudes. La sesión sólo está disponible en el controlador y la vista y se puede utilizar uno de una serie de diferentes mecanismos de almacenamiento: ActionDispatch::Session::CookieStore - Almacena todo en el cliente. ActionDispatch::Session::CacheStore- almacena los datos en la caché de Rails. 33 ActionDispatch::Session::ActiveRecordStore- almacena los datos en una base de datos usando Active Record. (requiere la gema activerecord-session_store). ActionDispatch::Session::MemCacheStore- almacena los datos en un clúster de memcached . Todas las sesiones store utilizan una cookie para almacenar un identificador único para cada sesión (debe utilizar una cookie, Rails no permitirá que se pase el identificador de sesión en la URL, ya que es menos seguro). Para la mayoría de las stores, este identificador se utiliza para buscar los datos de la sesión en el servidor, por ejemplo, en una tabla de la base de datos. Hay una excepción, y es el valor por defecto y de almacenamiento de sesión recomendado - el CookieStore - que almacena todos los datos de la sesión en la propia cookie (el ID todavía está disponible si lo necesita). Esto tiene la ventaja de ser muy sutil y requiere de configuración cero en una nueva aplicación con el fin de utilizar la sesión. Los datos de cookie se firman criptográficamente para que sea a prueba de manipulaciones. Y también está cifrada para que cualquier persona con acceso a la misma no pueda leer su contenido. (Rails no lo aceptará si ha sido editado). El CookieStore puede almacenar alrededor de 4 KB de datos - mucho menos que los otros - pero esto suele ser suficiente. El almacenamiento de grandes cantidades de datos en la sesión no se recomienda sin importar el almacenamiento de sesión que utiliza su aplicación. Especialmente debe evitar el almacenamiento de objetos complejos en la sesión (que no sean objetos de Ruby básicos, el ejemplo más común son instancias de modelo), ya que el servidor podría no ser capaz de volver a unirlos entre las solicitudes, lo que resultará un error. Si sus sesiones de los usuarios no almacenan datos críticos o no necesitan estar allí por largos períodos de tiempo (por ejemplo, si sólo se utiliza el flash para la mensajería), se puede considerar el uso de ActionDispatch::Session::CacheStore. Esto almacenará sesiones utilizando el caché que se ha configurado para su aplicación. La ventaja de esto es que se puede utilizar su infraestructura de caché existente para almacenar las sesiones sin requerir ninguna configuración o administración adicional. La desventaja, por 34 supuesto, es que las sesiones serán efímeras y podrían desaparecer en cualquier momento. (DEV & Noria, s.f.) Routes (Rutas) “El framework como tal trae documentación que corresponde a las diferentes formas como configurar el archivo de las rutas que se encuentra en el archivo config/routes.rb” (DEV & Noria, s.f.). Cuando el framework recibe una petición de parte del usuario para desplegar cierta información lo primero que analiza es la ruta que se escribió en el navegador, principalmente se enfoca en lo que esta después del dominio. Aparte del path o ruta también se analiza el verbo (métodos de petición) de http con la que la petición se envió, http es un protocolo orientado a transacciones y sigue el esquema petición-respuesta entre un cliente y un servidor. Entre los verbos con los que rails trabaja son: GET, POST, PATCH, PUT, DELETE entre otras. En Rails, una ruta de recursos proporciona una correspondencia entre los verbos HTTP y URLs a las acciones de controladores. Por convención, cada acción también se asigna a las operaciones CRUD particulares en una base de datos. Una sola entrada en el archivo de rutas, tales como: resources :products Crea siete rutas diferentes en su aplicación, todo el mapeo del controlador products: HTTP verbal GET GET Camino POST GET GET /products products#create /products/:id products#show /products/:id/editproducts#edit /products /products/new PATCH/PUT /products/:id DELETE /products/:id controlador Acción products#index products#new products#update products#destroy #Usado para mostrar una lista de todos los productos devolver un formulario HTML para la creación de un nuevo producto crear un nuevo producto mostrar un producto específico devolver un formulario HTML para editar un producto actualizar un producto específico eliminar un producto específico Tabla 1. Rutas de recursos (DEV & Noria, s.f.) 35 Por defecto, Rails crea rutas para las siete acciones predeterminadas (index, show new, create, edit, update y destroy). Se puede utilizar las opciones :only y :except para afinar este comportamiento. La opcion :only le dice a Rails para crear sólo las rutas especificadas: resources :products, only: [:index, :show] La opcion :except especifica una ruta o lista de rutas que Rails no debería crear: resources :photos, except: :destroy Si la aplicación tiene muchas rutas RESTful, usando :only y :except que generan sólo las rutas que en realidad se necesita se puede reducir el consumo de memoria y acelerar el proceso de enrutamiento. 36 2. METODOLOGÍA DE DESARROLLO 2.1 Tipo de la Investigación La investigación es aplicada y bibliográfica la que en primera instancia busca transformar el conocimiento puro existente en conocimiento útil y tiene como finalidad la búsqueda y consolidación del saber. Según su finalidad es aplicada ya que “busca la aplicación o utilización de los conocimientos que se adquieren” (BEHAR, 2008). Según su enfoque bibliográfica ya que: “La adquisición u obtención del conocimiento, la fijación, organización y ampliación del mismo así como su transmisión, requieren de normas especiales, de una metodología que precise y eduque en pensamiento y la expresión, que los estimulen y fortalezcan. Así pues, el método es un proceso lógico, surgido del raciocinio y de la inducción” (DE LA TORRE VILLAR & NAVARRO DE ANDA, 1990). 2.2 Población Son los documentos existentes en bibliografía escrita sobre Ruby on Rails versión 4, existentes en la web de manera libre. La selección se realizará utilizando el: criterio de la pertinencia significa que las fuentes consultadas deben ser acordes con el objeto de investigación y con sus objetivos, en tanto en cuanto aportar conocimientos, enfoques, teorías, conceptos y/o experiencias significativas para (RODRIGUEZ U, 2013) 37 fundamentar la propia investigación. 2.3 Método de Investigación El método empleado es analítico, lo que se busca es partir del todo que es el desarrollo sobre Ruby on Rails y analizar sus partes para conocer, su funcionamiento, vinculaciones y cualidades. Analítico: El Método analítico es aquel método de investigación que consiste en la desmembración de un todo, descomponiéndolo en sus partes o elementos para observar las causas, la naturaleza y los efectos. El análisis es la observación y examen de un hecho en particular. Es necesario conocer la naturaleza del fenómeno y objeto que se estudia para comprender su esencia. Este método nos permite conocer más del objeto de estudio, con lo cual se puede: explicar, hacer analogías, comprender mejor su comportamiento y establecer nuevas teorías. (ORTIZ & GARCIA, 2005) La investigación se realiza en torno a la búsqueda de información sobre el desarrollo de aplicaciones web utilizando Ruby on Rails. Se realiza la búsqueda en la web y se aplicará los detalles con el desarrollo del ejemplo práctico, el mismo que viajará durante todo el desarrollo de la investigación. 38 3. CALCULOS Y RESULTADOS En lo que sigue se realiza paso a paso dos ejemplos: el primero en el que se explica básicamente la funcionalidad de Rails y en el segundo una aplicación más completa que utiliza en lo posible todo lo expuesto en el Marco teórico. 3.1 Paquete de Instalación Iniciarse en Rails sobre Windows es relativamente fácil gracias a los esfuerzos de Engine Yard (http://engineyard.com) que han desarrollado RailsInstaller (http://railsinstaller.org/), un instalador en un sólo paquete de todas las herramientas que se necesita para ponerle a trabajar, incluido Rails. En su última versión railsinstaller 3.1.1 se encuentra: Ruby 2.1.8, Rails 4.2.5.1, activerecord-sqlserver-adapter 4.2.6, coffee-rails 4.1.1, jquery-rails 4.1.0, sass-rails 5.0.4, SQLite 3.8.7.2, MySQL 5.6.21, Bundler, Git, TinyTDS y DevKit 3.2 Instalación y configuración Ejecutar el archivo de instalación desde la ubicación donde se encuentre descargado. Realizar la instalación típica de Windows. Figura 8. Ventana de instalación de Ruby/Rails 39 Escoger la ubicación para la instalación y seleccionar todas las opciones e instalar Figura 9. Ventana para seleccionar la ubicación Al finalizar la instalación, se muestra la consola de sistema CMD o terminal para configurar los datos de Git (nombre, email). Para poder verificar las versiones de Ruby y Rails se ejecuta los siguientes comandos. Con lo que se puede verificar la instalación correcta de Ruby y Rails. C:\Sites>ruby –v C:\Sites>rails -v El siguiente paso es instalar las gemas necesarias para el funcionamiento, que se las puede descargar desde la página siguiente. https://rubygems.org/pages/download Descomprimir el archivo .zip de gemas, ingresar al directorio desde el terminal y ejecutar el siguiente código: C:\rubygems-2.5.2>ruby setup.rb Para finalizar la instalación y evitar posibles errores con MySQL se ejecuta: C:\Sites>gem install mysql2 40 Es necesario instalar el programa nodejs que se lo puede descargar desde (https://nodejs.org/en/) esto es de ayuda en la solución de posibles errores, la instalacion es muy sencilla, al terminar la instalación y si el terminal CMD se encuentra en ejecución cerrarlo y volverlo a abrir para que los cambios tengan efecto. 3.3 Mi primera aplicación 3.3.1 Creación de los recursos Para crear varios procesos en Rails se usará el símbolo de sistema (cmd) de Windows se inicia presentando algunos comandos básicos. CD.- Muestra el nombre o cambia al directorio actual. CLS.- Borra los símbolos o el texto en la pantalla de la consola. COPY.- Copia uno o más archivos en otra ubicación. DEL.- Elimina uno o más archivos. DIR.- Muestra una lista de archivos y subdirectorios en un directorio. Rails tiene una serie de comandos llamados generadores que están diseñados para hacer el desarrollo más fácil mediante la creación de todo lo que es necesario para empezar a trabajar en una tarea en particular. Uno de ellos es el generador de nuevas aplicaciones, lo que le proporcionará la base de una aplicación Rails. Para utilizar este generador, se tiene que abrir un terminal CMD, navegar a un directorio en el que se tenga derechos para crear archivos, por lo general Rails crea una carpeta llamada Sites en el disco local C:, para situarnos en ella se debe editar lo siguiente: cd c:\Sites, la cual se usa para comenzar con la descripción de la herramienta, entonces para crear una nueva aplicación se escribe: C:\Sites>rails new AppWeb esto creará una aplicación Rails llamada AppWeb en el directorio AppWeb e instala automáticamente las dependencias de gemas utilizando el comando internamente bundle install. 41 Opcionalmente puede ver todas las opciones de línea de comandos que el generador de aplicaciones Rails acepta mediante la ejecución de: C:\Sites>rails new -h Después de crear la aplicación, debe ingresar al directorio AppWeb, así: C:\Sites>cd AppWeb El directorio de AppWeb tiene un número de archivos y carpetas generadas automáticamente que componen la estructura de una aplicación Rails. Lo que se vería de la siguiente forma en una ventana de Windows. Figura 10. Directorio AppWeb La mayor parte del trabajo va a pasar en la carpeta app, pero aquí hay un resumen básico de la función de cada uno de los archivos y carpetas que Rails creó de forma predeterminada: 42 Carpeta app / Propósito Contiene los controladores, modelos, vistas, ayudas, entre otros para su aplicación. La mayoría de actividades se centra en esta carpeta. bin/ Contiene los script de rails para ejecutar la aplicación y puede contener otras script de comandos que se utilizan para la configuración e implementación de la aplicación. config / Configurar las rutas de su aplicación, base de datos, y mucho más. config.ru Configuración de rack para servidores basados en rack utilizados para iniciar la aplicación. db / Contiene el esquema de base de datos actual, así como las migraciones de bases de datos. Gemfile Estos archivos permiten especificar qué dependencias de gemas se Gemfile.lock desee para su aplicación Rails. Estos archivos son utilizados por la gema Bundler. lib / Módulos de expansión para su aplicación. log/ Archivos de registro de aplicación. public/ La única carpeta visible para el público. Contiene los archivos estáticos y activos compilados. Rakefile Este archivo localiza y carga las tareas que se pueden ejecutar desde la línea de comandos. Las definiciones de tareas se definen a través de los componentes de rails. README.rdoc Este es un breve manual de instrucciones para su aplicación. Debe editar este archivo para decir a los demás lo que hace su aplicación, cómo configurarlo, y así sucesivamente. test/ Las pruebas unitarias, accesorios y otros equipos de prueba. tmp/ Los archivos temporales (como los archivos de caché, pid, y de sesión). vendor/ Un espacio para todo el código de terceros en una aplicación típica rails esto incluye gemas externas. Tabla 2. Archivos y Carpetas creadas Para empezar, es necesario presentar un poco de texto en la página web rápidamente. Para ello, es necesario tener el servidor de aplicaciones de Rails en ejecución, procedimiento que se explica en lo que sigue. 3.3.2 Poner en marcha el servidor Web De hecho, se tiene una aplicación Rails funcional. Para verlo, se necesita iniciar un servidor web en el equipo de desarrollo. Puede hacer esto mediante la ejecución del siguiente comando en el directorio de la aplicación: C:\Sites\AppWeb>rails server O a su vez en su forma más compacta rails s 43 Esto lanzará WEBrick, un servidor web distribuido con Ruby de forma predeterminada. Para ver su aplicación en acción, abra una ventana del navegador y vaya a http://localhost:3000. Debería ver la página de información por defecto de Rails así: Figura 11. WEBrick Si el servidor no pudo iniciar y muestra un error que informa que corra el comando bundle install para solucionarlo, esto se debe por alguna incompatibilidad de la versión de Windows por lo que no pudo ejecutar automáticamente el comando bundle install al crear el proyecto. Para detener el servidor web, pulse Ctrl + C en la ventana de terminal en el que se está ejecutando. En el modo de desarrollo, Rails por lo general no requiere que se reinicie el servidor; los cambios que realice en los archivos serán recogidos automáticamente por el servidor. La página "Home" es la prueba de fuego para una nueva aplicación Rails. Se asegura de que tiene su software configurado correctamente suficiente para ejecutar una página web. Lo que se vera en lo que sigue. 44 3.3.3 Hola, Rails Para mostrar a Rails diciendo "Hola, Rails!", se necesita crear, como mínimo, un controlador y una vista. El propósito de un controlador es recibir solicitudes específicas para la aplicación. El enrutamiento (Routing) decide cuál controlador recibe lo que se pide. A menudo, hay más de una ruta para cada controlador, y las diferentes rutas pueden servir a diferentes acciones. El propósito de cada acción es recoger información para proporcionarla a una vista. El propósito de una vista es para mostrar la información en un formato legible. Una distinción importante es que donde se recoge la información o datos que desea administrar es el controlador, no la vista. La vista simplemente debe mostrar esa información. Para crear un nuevo controlador, se tendrá que ejecutar el generador de "controladores" y decirle que quiere un controlador llamado "home" con una acción llamada "index”. C:\Sites\AppWeb> rails generate controller home index Rails creará varios archivos en los directorios /app/controllers/ y /app/views/ este último contiene una carpeta con el nombre del controlador en este caso home. Los archivos más importantes de éstos en app/controllers/home_controller.rb y son, el la vista, controlador, que se ubicado encuentra en app/views/home/index.html.erb. Abrir el archivo app/views/home/index.html.erb en el editor de texto de su preferencia (SublimeText, Brackets, Notepad ++, entre otros). En lo que sigue se trabaja en el directorio C:\Sites\AppWeb que es donde se encuentran todos los archivos necesarios. Ahora en el archivo abierto eliminar todo el código existente en el archivo, y reemplazarlo con la siguiente línea de código: <h1> Hola, Rails!</h1> 45 Configuración de la Página de Inicio (Root) Ahora que se creó el controlador y vista, se tiene que decirle a rails cuando se requiere mostrar la vista que contiene “Hola, Rails!". En este caso, se quiere que aparezca cuando se navegue a la URL raíz o root del sitio web, http://localhost:3000. Por el momento, " la página de información de rails Welcome Aboard" está ocupando ese lugar. Ahora, se tiene que indicarle a rails donde se encuentra su página principal home. Para lo cual se debe abrir el archivo config/routes.rb. Este es el archivo de enrutamiento de la aplicación, que contiene entradas con un DSL especial (lenguaje específico de dominio) que le indica a rails cómo conectar las solicitudes entrantes a controladores y acciones. En el archivo se debe escribir root 'home#index' lo cual le indica a rails asociar peticiones de la raíz de la aplicación a la acción index del controlador home y además get 'home/index' le indica a rails asociar solicitudes al url http://localhost:3000/home/index de la acción index del controlador. Este último se creó cuando se ejecutó el generador del controlador (rails generate controller home index). Poner en marcha el servidor web nuevamente si se lo detuvo para generar el controlador y luego dirigirse al url http://localhost:3000 en su navegador. Se muestra el mensaje "Hola, Rails!" que se escribió en app/views/home/index.html.erb, lo que indica que esta nueva ruta es en realidad la acción del index de homeController y está prestando la vista correctamente. Figura 12 Hola Rails! 46 Con esto se ha realizado la primera parte del ejemplo utilizando Ruby on Rails y mostrando la funcionalidad básica del mismo. 3.4 Aplicación web completa Se desarrolla una aplicación web utilizando en lo posible toda la teoría expuesta en el capítulo 1. Se realizará un inventario de productos de una empresa distribuidora de los mismos, a la cual se integran productos de diferentes proveedores, fabricantes y tipos. Además contendrá un sistema de autenticación de usuarios. En la aplicación el administrador podrá ser capaz de ver, crear, eliminar y actualizar los datos determinados de producto, proveedor, fabricante y tipo. Para ver más a detalles el ejemplo con sus diagramas de flujo y casos de uso dirigirse al Anexo E. Configurar la conexión a la base de datos MySQL Cabe recordar que se necesita que se encuentre instalado el motor de base de datos MySQL para que las configuraciones se ejecuten con normalidad, se lo puede descargar desde la url http://dev.mysql.com/downloads/windows/installer/5.7.html Ahora bien buscar el archivo en la ruta conf/database.yml y remplazar el código: development: <<: *default database: db/development.sqlite3 por lo siguiente: development: adapter: mysql2 username: root password: 1234 host: 127.0.0.1 port: 3306 database: appWeb #nombre de su base de datos pool: 5 timeout: 5000 se tiene que asegurar de que exista dos espacio antes de la palabra desde la segunda hasta la última línea del código anterior para evitar errores, cambiar username, password 47 por los datos de acceso a su base de datos y en database escribir el nombre de la base de datos que se quiere crear, recuerde guardar los cambios realizados en este archivo. Luego se aumenta estas líneas en el archivo /GemFile gem 'mysql2' y ejecutar el comando siguiente, para instalar las nuevas gemas que se ha colocado, si el servidor se encuentra activo se debe detener: C:\Sites\AppWeb> bundle install La línea de código anterior se la debe ejecutar cada vez que se coloque una nueva gema. Una vez instalada la gema, se las actualiza utilizando el siguiente código para solucionar posibles incompatibilidades con gemas instaladas anteriormente: C:\Sites\AppWeb> bundle update Para construir la base de datos desde cero a partir de la configuración realizada en el archivo config/database.yml; se ejecuta el comando de la siguiente forma: C:\Sites\AppWeb> rake db:create lo que realizo la línea anterior es crear la base de datos en el gestor de base de datos MySQL. Sin que se haya ni siquiera ingresado al mismo MySQL. Si no se pudo ejecutar el comando rake db:create debido a un error, ejecutar el comando gem update –system para resolverlo y con esto ya se podrá ejecutar. Ahora se crea los modelos, las vistas y los controladores, para el ejemplo planteado, según el siguiente esquema: 48 Figura 13. Modelo Entidad Relación 3.4.1 Creación de Modelos Mediante el generador scaffold se crean los modelos, las vistas y los controladores, este generador es de gran ayuda para no realizar una por una cada acción. Rails está familiarizado con el idioma ingles para realizar las convenciones sobre la singularidad y la pluralidad de los modelos, por lo dicho algunos modelos no se plurizaran correctamente en este caso Proveedor se plurizara como Proveedors. Ahora para el ejemplo se ejecuta el generador scaffold para el modelo TipoProducto que contiene los campos nombre y descripción. C:\Sites\AppWeb>rails generate scaffold TipoProducto nombre:string descripcion:text Luego para migrar el modelo creado en el paso anterior a la base de datos MySQL se ejecuta lo siguiente, ya que scafford también creo la migración automáticamente: C:\Sites\AppWeb>rake db:migrate Cada vez que se ejecute el generador scaffold se debe realizar la migración con la línea que antecede. La creación correcta del modelo TipoProducto, puede verificar en la url http://localhost:3000/tipo_productos, con el servidor en ejecución. Se presenta una página web con las acciones de crear, leer, actualizar y eliminar: 49 Figura 14. Vista Tipos de Productos Ahora nuevamente utilizando el generador scaffold se crea el modelo Fabricante con los campos nombreFab y descripcionFab. C:\Sites\AppWeb>rails generate scaffold Fabricante nombreFab:string descripcionFab:text migrar el modelo creado (rake db:migrate). Se verifica la creación del modelo en http://localhost:3000/fabricantes lo que muestra. Figura 15. Vista de Fabricante Para el ejemplo que se desarrolla se crea con scaffold todos los modelos con lo que automáticamente se crean las vistas y los controladores. 50 3.4.2 Modelo uno a muchos Se crea el modelo Producto que contiene los campos nombre, descripcion, precio, fecha, tipo_producto y fabricante estos dos últimos son campos que hacen referencia a la asociación de 1:n (uno a muchos) con los otros modelos creados anteriormente y automáticamente los vincula con sus foreign_key, también coloca las asociaciones en su controlador: C:\Sites\AppWeb>rails generate scaffold Producto nombre:string descripcion:text precio:decimal fecha:time cantidad:integer tipo_producto:references fabricante:references migrar el modelo creado (rake db:migrate) y verificar el modelo en la url http://localhost:3000/productos, lo que muestra. Figura 16. Vista Producto Tener en cuenta que en la creación de modelo producto hace referencia entre tipoProducto y fabricante con el comando :reference esto es lo que produce la asociación de uno a muchos. 51 3.4.3 Modelo muchos a muchos Crear el modelo para el proveedor Proveedor que contiene los campos nombre, ruc, telefono, dirección, email y ciudad, se usa este modelo para vincular la asociación de muchos a muchos con el modelo Producto. C:\Sites\AppWeb>rails generate scaffold Proveedor nombre:string ruc:integer telefono:integer direccion:text email:string ciudad:string migrar el modelo creado (rake db:migrate). Se podrá ver las vistas del modelo en la url http://localhost:3000/proveedors como se muestra a continuación. Figura 17. Vista Proveedor Como una asociación de muchos a muchos necesita un modelo intermedio para la asociación entre estos modelos, se crea el modelo que se usará como conexión entre Proveedor y Producto, el modelo se lo define de la siguiente forma: C:\Sites\AppWeb>rails g migration CreateJoinTableProductoProveedor producto proveedor migrar el modelo creado (rake db:migrate). También se debe colocar la asociación en cada uno de los modelos, escribir en los archivos de modelos que se encuentran en el directorio /app/models como se observa: 52 class Proveedor < ActiveRecord::Base has_and_belongs_to_many :productos end y class Producto < ActiveRecord::Base belongs_to :tipo_producto belongs_to :fabricante has_and_belongs_to_many :proveedors end como último paso en el archivo /app/controller/productoController.rb se tiene que crear el parámetro {:proveedor_ids => []} que recibirá el controlador de Producto dentro del método producto_params quedando de la siguiente forma: def producto_params params.require(:producto).permit(:nombre, :descripcion, :precio, :fecha, :tipo_producto_id, :fabricante_id, {:proveedor_ids => []}) end con lo que se finaliza la creación de los modelos, las vistas y los controladores que gracias al generador scafford se realizó de una manera más sencilla. 3.4.4 Actualizar o crear campos Al realizar este proceso de generación de modelos a veces se puede omitir la creación de algún campo o su vez querer cambiar el nombre o tipo de dato de los que se ha creado, para ello Rails tiene algunas funcionalidades específicas. Para nuestro ejemplo se cambia el tipo de dato del campo fecha de la tabla productos: del tipo time a date, y se aumentará los números de caracteres del campo ruc en el modelo proveedors. Generar lo siguiente: C:\Sites\AppWeb>rails g migration cambiar_tipos_de_datos lo que genera es un archivo en el directorio /db/migrate/ con el nombre YYYYMMDDhhmmss_cambiar_tipos_de_datos.rb, el cual será editado para realizar los cambios previstos: 53 class CambiarTiposDeDatos < ActiveRecord::Migration def change change_column :productos, :fecha, :Date change_column :proveedors, :ruc, :integer, :limit => 8 change_column :proveedors, :telefono, :integer, :limit => 8 end end y como se ha venido repitiendo se debe realizar la migración, C:\Sites\AppWeb>rake db:migrate cabe recordar que si crea un campo nuevo en cualquier modelo, se tiene que agregar a los parámetros que recibirá el controlador, por ejemplo si se crea un campo link en productos se lo agregaría en el método producto_params, ya que sin este no podrá recibir la solicitud para guardarse en la base de datos: def producto_params params.require(:producto).permit(:nombre, :descripcion, :precio, :fecha, :tipo_producto_id, :fabricante_id,:link ) end este código no es parte del ejemplo que se está desarrollando pero es fundamental en la creación de nuevos campos. Una forma genérica para cambiar el nombre, el tipo de dato, o agregar un campo en primera instancia crear un archivo de migración que se lo realiza de la siguiente forma: C:\Sites\AppWeb>rails g migration nombre_migracion Así mismo existen varios comandos para diferentes funcionalidades como por ejemplo renombrar, crear o cambiar el tipo de un campo, change_column :nombreTabla, :nombreColumna, :tipoDato rename_column :nombreTabla, :nombreColumna, :nuevoNombreColumna add_column :nombreTabla, :nombreColumna, :tipoDato Ejemplo: change_column :products, :fecha, :Date rename_column :product_types, :descripcion, :descripcionType add_column :manufacturers, :link, :text 54 3.4.5 Crear Campos para archivos Existe una forma de crear campos para el manejo de archivos, pero primero se deben instalar algunas gemas para que funcione correctamente. Se empieza por instalar la gema paperclip pero esta funciona con la aplicación ImageMagick por lo que primero se debe instalar esta aplicación, ImageMagick está distribuido para Windows, Linux y MAC. La aplicación que se instalará es ImageMagick6.9.3-7-Q16-x64-dll.exe en su versión para Windows y se la puede encontrar en el link http://www.imagemagick.org/script/binary-releases.php#windows La instalación de esta aplicación es la típica de Windows con solo seguir el botón next. Figura 18. ImageMagick Para un buen funcionamiento con Rails deben estar seleccionados las opciones que se muestran en la siguiente imagen, y finalmente completar la instalación, Figura 19. ImageMagick Tasks 55 para comprobar que la aplicación se instaló correctamente se debe abrir el terminal (si se encuentra abierto se lo debe cerrar y volver a abrir para que los cambios surtan efecto) y escribir convert –version con lo cual nos muestra la versión de la aplicación. C:\Sites\AppWeb>convert -version Ahora se debe editar el archivo de GemFile y colocar la gema paperclip, gem 'paperclip' Luego de haber colocado la gema se debe instalar con el comando: bundle install Además para corregir errores de compatibilidad con la versión 4 de paperclip se debe crear manualmente un archivo llamado paperclip.rb en el directorio /config/initializer/ que contendrá el siguiente código: require 'paperclip/media_type_spoof_detector' module Paperclip class MediaTypeSpoofDetector def spoofed? false end end end ya realizada la instalación ahora se crea el campo de manejo de archivos para el ejemplo. Se crea un campo para el manejo de la imagen del fabricante y el producto llamado urlImagen; primero se crea el archivo de migración como lo se vio anteriormente. C:\Sites\AppWeb>rails g migration add_urlImagen lo que genera es un archivo en el directorio /db/migrate/ con el nombre YYYYMMDDhhmmss_ add_urlImagen.rb, el cual será editado para realizar los cambios previstos: class AddUrlImagen < ActiveRecord::Migration def change add_attachment :fabricantes, :urlImagen add_attachment :productos, :urlImagen end end 56 luego migrar el archivo (rake db:migrate), con lo cual se crean 4 campos en cada tabla de la base de datos que sirven para el manejo de los datos de archivos por parte de la gema paperclip, esto lo realiza automáticamente: urlImagen_file_name, urlImagen_content_type, urlImagen_file_size y urlImagen_updated_at Como se dijo anteriormente se debe adicionar el parámetro en el método de su controlador respectivo, el parámetro debe tener el nombre del campo urlImagen como se observa en cada uno de los respectivos controladores: def fabricante_params params.require(:fabricante).permit(:nombreFab, :descripcionFab, :urlImagen) end ----------------------------------------------------------------def producto_params params.require(:producto).permit(:nombre, :descripcion, :precio, :fecha, :tipo_producto_id, :fabricante_id, {:proveedor_ids => []}, :urlImagen) end Finalmente se debe agregar las validaciones que paperclip necesita en el modelo fabricante y producto respectivamente para que integre toda la funcionalidad de la gema: class Fabricante < ActiveRecord::Base has_attached_file :urlImagen, style: {medium: "1280x720"} validates_attachment_content_type :urlImagen, :content_type => /\Aimage/ end -----------------------------------------------------------------class Producto < ActiveRecord::Base belongs_to :tipo_producto belongs_to :fabricante has_and_belongs_to_many :proveedors has_attached_file :urlImagen, style: {medium: "1280x720"} validates_attachment_content_type :urlImagen, :content_type => /\Aimage/ end basicamente la primera línea informa que tiene un archivo adjunto que se vincula con el campo que se creó, además se le ha colocado la opción de estilos para el manejo de imágenes por ImageMagick que lo que hace es; a cualquier imagen que se cargue se cree una copia con las dimensiones especificadas, esta última parte es opcional, la segunda línea valida el campo y el contenido que debe tener en este caso solo imágenes. 57 3.4.6 Manejo de Vistas Ahora se comienza a editar las vistas para aumentar los campos necesarios o cambiar lo que creó automáticamente scaffold. Primero se debe verificar que en el directorio /app/view se han creado varios directorios con los nombre de los modelos, además en el interior de cada uno se encuentran varios archivos con extensión html.erb entre los que se destacan index, edit, new, show y una vista parcial _form, el código de estos archivos son muy entendibles, así que no será difícil saber qué hace cada uno. Vista Fabricante Crear el campo para el manejo de archivos en la vista fabricante. Editar el archivo parcial _form.html.erb de fabricantes para colocar el campo de imagen que se creó en el modelo, colocando el código: <div class="field"> <%= f.label 'Url Imagen' %><br><%= f.file_field :urlImagen %> </div> Visualizándolo de la siguiente manera: Figura 20. Vista Fabricante Editada Aumentar en el archivo show.html.erb el código para visualizar la imagen que se cargó en el modelo: <p><strong>Imagen:</strong></p> <div><%= image_tag @fabricante.urlImagen.url() %></div> Resultado lo siguiente: 58 Figura 21. Vista Fabricante Show y se puede colocar en el archivo index.html.erb el siguiente código, para visualizarlo en la tabla de todos los registros manufacturers, en este caso se lo aumenta antes de las etiquetas link_to, de la siguiente forma. <td><%= image_tag fabricante.urlImagen.url() %></td> Además se puede aumentar la clase a la etiqueta de tabla en todas las vistas así <table class="table table-bordered table-striped"> Figura 22. Vista Fabricante Index 59 Vista Producto A esta vista se le realizará varios cambios ya que el modelo contiene varias asociaciones. En el archivo parcial _form.html.erb de producto se realiza los siguientes cambios: Remplazar la etiqueta de entrada de precio que es tipo texto a números decimales <%= f.text_field :precio %> Remplazar por: <%= f.number_field :precio, :class =>'text_field', :step => 0.1 %> como se cambió el tipo de dato de fecha también se debe cambiar la etiqueta de entrada: <%= f.time_select :fecha %> Remplazar por: <%= f.date_select :fecha %> Para que nos muestre los valores de la asociación de tipo de producto y fabricante en un elemento seleccionable que resulta una forma más amigable se remplaza los campos: <%= f.text_field :tipo_producto_id %> -----------------------------------------------------------------<%= f.text_field :fabricante_id %> por los siguientes respectivamente: <%= collection_select(:producto, :tipo_producto_id, TipoProducto.all, :id, :nombre,{},{:multiple=>false}) %> ---------------------------------------------------------------<%= collection_select(:producto, :fabricante_id, Fabricante.all, :id, :nombreFab,{},{:multiple=>false}) %> Colocar el campo de imagen que se creó en el modelo, escribiendo el código: <div class="field"> <%= f.label 'Url Imagen' %><br><%= f.file_field :urlImagen %> </div> Para mostrar la asociación de muchos a muchos con proveedor se inserta el código: 60 <div class="field"> <%= f.label :Proveedor %><br> <% for proveedor in Proveedor.all %> <tr> <td><%= check_box_tag "producto[proveedor_ids][]", proveedor.id, @producto. proveedors.include?(proveedor) %></td> <td><%= proveedor.nombre %></td> </tr> <% end %> </div> Con lo que la vista se vería de la siguiente forma Figura 23. Vista Producto Editada En el archivo show.html.erb remplazar: <p><strong>Tipo producto:</strong><%= @producto.tipo_producto %> </p> <p><strong>Fabricante:</strong> <%= @producto.fabricante %></p> por: 61 <p> <strong>Tipo de Producto:</strong> <%= @producto.tipo_producto.nombre %> </p> <p> <strong>Fabricante:</strong> <%= @producto.fabricante.nombreFab %> </p> <h3>Proveedores</h3> <ul> <% @producto.proveedors.each do |proveedor| %> <li><%= proveedor.nombre %></li> <% end %> </ul> <p> <strong>Imagen:</strong> </p> <div><%= image_tag @producto.urlImagen.url() %></div> Lo que se visualizaría de la siguiente forma. Figura 24. Vista Producto Show y se puede colocar en el archivo index.html.erb el siguiente código, para visualizar en la tabla de todos los registros productos, en este caso se lo aumenta antes de las etiquetas link_to, de la siguiente forma. <td><%= producto.tipo_producto.nombre %></td> <td><%= producto.fabricante.nombreFab %></td> <td><%= image_tag producto.urlImagen.url() %></td> 62 Quedando finalmente la página de productos así. Figura 25. Vista Producto Index 3.4.7 Assets En el directorio /app/assets, se guardan los estilos y JavaScrit que se utilizan en la aplicación. Dentro de assets, se encuentran tres carpetas: images, javascripts y stylesheets. Rails carga automáticamente las imágenes, JavaScripts y CSS que se encuentran en las carpetas descritas y respectivamente. En esta aplicación se utiliza las herramientas de diseño CSS y JavaScript predefinidos de Bootstrap. Que se las puede descargar de: http://getbootstrap.com/, como sugerencia puede el lector ingresar a http://www.w3schools.com/bootstrap/ para aprender más sobre Bootstrap. Descomprimir el archivo .zip descargado y copiar los CSS y JavaScript que este contiene a las respectivas carpetas de asset. Adicionalmente se utiliza flexbox grid que nos facilita los CSS necesarios para trabajar con bloques, se puede descargar de: http://flexboxgrid.com/, descomprimir el archivo .zip descargado y copiar el archivo flexboxgrid.css que se encuentra en la carpeta css a la carpeta respectiva de asset. Menú de Navegación con Bootstrap Crear una carpeta llamada parcial en el directrio /app/view, en el cual se creará un archivo parcial llamado _cabecera.html.erb dentro de este se creará el menú de navegación, con el siguiente código: 63 <div class="row center-xs"> <div class="col-sm-10"> <nav class="navbar navbar-inverse"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="#">AppWeb</a> </div> <ul class="nav navbar-nav"> <li class="active"><%= link_to "Home",root_path,:class => "navbar-brand", target:"_parent" %></li> <li><%= link_to "Productos",productos_path, target:"_parent" %></li> <li><%= link_to "Fabricante",fabricantes_path, target:"_parent" %></li> <li><%= link_to "Tipos de Productos",tipo_productos_path, target:"_parent" %></li> <li><%= link_to "Proveedores",proveedors_path, target:"_parent" %></li> </ul> </div> </nav> </div> </div> ahora se llama a esta vista parcial desde el archivo /layouts/application.html.erb. Application es la vista que contiene la estructura de la página web para todas las demás vistas, se colocará el menú en esta vista para que se replique en las demás vistas, así: <%= render "parcial/cabecera"%> Quedando de la siguiente manera: <!DOCTYPE html> <html> <head> <title>AppWeb</title> <%= stylesheet_link_tag 'application', media: 'all', 'dataturbolinks-track' => true %> <%= javascript_include_tag 'application', 'data-turbolinkstrack' => true %> <%= csrf_meta_tags %> </head> <body> <%= render "parcial/cabecera"%> <%= yield %> </body> </html> 64 Ahora puede ver el menú de navegación en todas las vistas de la aplicación. Figura 26. Menu de Navegación 3.4.8 Validaciones En esta sección se crean las validaciones para los modelos creados. Como este es un ejemplo se desarrollan las validaciones básicas para la administración de cada modelo. En el directorio /app/models/ se aumenta el código en los archivos de cada modelo. Fabricante El siguiente código realiza 3 validaciones la primera, es que la imagen del fabricante se cargue correctamente, la segunda y tercera se realizan sobre el mismo campo valida que el nombre no se encuentra vacío y que este sea único, validates :urlImagen,presence: true validates :nombreFab, presence: true, :uniqueness=> { :case_sensitive => true } Tipo de producto En tipo de producto se realizan 3 validaciones sobre el campo nombre: que no sea vacío, que sea único y que contenga solo caracteres de letras: validates :nombre, presence: true, :uniqueness=> { :case_sensitive => true },format: { with: /\A[a-zA-ZñÑáéíóú]+\s?+[a-zAZñÑáéíóú]+\z/} 65 Proveedor Sobre el campo ruc la validación es que contenga entre 10 y 13 caracteres, y se muestra un mensaje cuando no cumple la validación. En el campo teléfono la validación es parecida a la anterior cambiando el número de caracteres entre 9 y 10. En el nombre se valida que no sea vacío y que sea único. En el mail valida que no esté vacío, que sea único y que tenga el formato de mail, si no pasa alguna validación dará un mensaje de error. En los campos de dirección y ciudad que no se encuentran vacíos. validates :ruc, length: { in: 10..13 , message: "debe tener entre 10 y 13 caracteres"} validates :telefono, length: { in: 9..10 , message: "debe tener entre 9 y 10 caracteres"} validates :nombre, presence: true,:uniqueness=> { :case_sensitive => true } validates :email, presence: true, :uniqueness=> { :case_sensitive => true }, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[az]{2,})\z/i, message: "El formato del correo es invalido" } validates :direccion, presence: true validates :ciudad, presence: true Producto En el campo nombre se valida que este no sea vacío y que sea único. El precio que no sea vacío y tenga formato decimal. La descripción que no sea vacío. El campo de imagen no este vacío. validates :nombre, presence: true, :uniqueness=> { :case_sensitive => true } validates :precio, presence: true, format: { with: /\d/} validates :descripcion, presence: true validates :urlImagen,presence: true De ahora en adelante no podrá crear ni actualizar registros en los modelos si no cumple las validaciones. Además el modelo producto solo crea o actualiza registros si los registros de los otros modelos con los cual esta asociado están validados correctamente. 3.4.9 Buscador y paginación El buscador permite encontrar de manera más ágil un producto, fabricante o un proveedor, desde la vista de cada modelo. 66 Es necesario instalar una gema en el archivo GemFile, la misma que provee de ayudas en el desarrollo del buscador. Para lo cual se debe escribir lo siguiente: gem 'kaminari' lo anterior solo define que se utilizará kaminari en la aplicación. Luego se ejecuta el siguiente código para instalar la nueva gema que se colocó: C:\Sites\AppWeb>bundle install Se realiza el buscador para el modelo Producto, en el archivo del controlador /app/controllers/productos_controller.rb se cambia el método index sustituyendo la línea: @productos = Producto.all por lo siguiente: if params[:search] @productos = Producto.search(params[:search]).order("nombre").page(params[:page ]).per(10) else @productos = Producto.order("nombre").page(params[:page]).per(10) end En el archivo de modelo /app/models/producto.rb crear el siguiente método: def self.search(search) where('nombre LIKE ?', "%#{search}%") end Ahora crear el buscador en la vista de Producto, editar el archivo index.html.erb, colocando lo siguiente sobre la tabla que se encuentra definida: <%= form_tag productos_path, :method=> "get" do %> <%= text_field_tag :search, params[:search], placeholder: "Busqueda por nombre" %> <%= submit_tag "Buscar",:nombre => nil %> <% end %> 67 con esto se puede ver el campo y el botón de búsqueda en la vista index del producto. Con la gema instalada previamente en esta sección, se puede crear también la paginación. Colocar el siguiente código luego de la tabla para crear la paginación, cabe decir que por la configuración en el controlador, realiza la paginación cada 10 registros: <%= paginate @productos %> Con lo expuesto en esta sección se encuentran funcional la búsqueda y paginación para el modelo Producto. Figura 27. Buscador Se debe usar el mismo proceso para realizar la búsqueda y paginacion de los otros modelos, cambiando lo que sea necesario para cada uno. 3.4.10 Autenticación de usuario Es necesario instalar las 3 gemas en el archivo GemFile, la misma que provee de ayudas en el desarrollo del registro y autenticación de usuario. De la siguiente manera: gem 'devise' gem 'devise-i18n' gem 'devise-i18n-views' lo anterior define que se utilizarán las 3 gemas de devise en la aplicación. 68 Instalar las gemas con lo siguiente: C:\Sites\AppWeb>bundle install Como se ha visto durante el desarrollo de este ejemplo cada vez que defina una gema se debe instalar con el código anterior. Esta gema requiere la instalación de toda su funcionalidad, así: C:\Sites\AppWeb>rails g devise:install una vez que finaliza la instalación aparecerá un mensaje con requisitos necesarios para para utilizar devise. Entre los cuales cabe destacar los más importantes: Que se encuentre establecida la ruta principal en este caso del ejemplo: root 'home#index'. Colocar las siguientes líneas de código en la ruta app/views/layouts/application.html.erb <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> Ahora ya puede crear el modelo para registro y autenticación de usuarios en este caso Usuario, use para esto el siguiente código: C:\Sites\AppWeb>rails g devise Usuario con lo anterior crea automáticamente: las vistas de inicio de sesión, registro de usuarios y recuperación de contraseña con los campos :email y :encrypted_password además de otros campos que los maneja automáticamente la gema. Devise crea una autenticación más robusta no así si se crea una autenticación desde cero, esta crea automáticamente los campos de contraseñas encriptados para que sea más seguro, además crea variables para que el usuario pueda recuperar su contraseña. Finalmente hay que migrar a la base de datos: rake db:migrate 69 Ahora ya se tiene la autenticación de usuarios y puede verla en http://localhost:3000/usuarios/sign_in, esta vista también tiene links para dirigirse a recuperar contraseña y registrarse. Si desea cambiarlos estilos de las vistas que genero device puede dirigirse al Anexo A para más información Figura 28. Inicio de Sesión Toda la lógica de sesiones está en devise, no piense que devise es solo un plugin, ya que este es de código abierto y se puede sobrescribir para que funcione como lo desee. Además devise da varios métodos útiles para el control de inicio de sesión como usuario_signed_in? , que verifica si el usuario inicio sesión o no correctamente, o current_usuario.email que muestra el email del usuario autenticado, se utiliza el código en el archivo parcial _cabecera.html.erb para demuestra lo expuesto: <% if usuario_signed_in? %> <%= current_usuario.email %> <%= link_to "Cerrar Sesion", destroy_usuario_session_path, method: :delete %> <%else %> <%= link_to "Iniciar Sesion", new_usuario_session_path %> <%= link_to "Registrarse", new_usuario_registration_path %> <% end %> 70 También se inserta el callback siguiente a los controladores que desee que no sea accesible sin antes estar autenticado, estos controladores se encuentran en el directorio /app/controllers/: before_action :authenticate_usuario! lo que hace es bloquear las rutas de los modelos de la aplicación, si se quiere ingresar sin antes haberse autentificado, y las redirige a la ventana de inicio de sesión. Figura 29. Autenticación Terminado esto tendría una aplicación funcional culminando la segunda parte del ejemplo, para ver el manejo de email ir a el Anexo C y para el manejo de sesiones ver ANEXO D, bastaría trabajar con los estilos para que la aplicación tenga una mejor visualización, esto dependerá del lector que puede hacerlo mediante CSS o gracias a las ayudas que trae bootstrap y flexbox Grid. Con esto se finaliza este tutorial de Ruby on Rails y es importante mencionar que se debe analizar bien el código que se ha colocado para poder entender la razón por la cual funciona y de esta manera generar conocimiento de calidad. Se adjunta el proyecto terminado y ademas el proyecto mejorado con estilos como se observa en las imágenes siguientes, las url de descarga se encuentran el la sección de recomendaciones. 71 72 Figura 30. Aplicación aplicado estilos 73 4. DISCUSIÓN Este trabajo tuvo como propósito presentar teorías fundamentales e introductorias sobre Ruby on Rails, las mismas que se utilizan en el desarrollo de un ejemplo paso a paso y que permite la introducción al desarrollo de aplicaciones web. Durante el desarrollo de esta guía se encontró muy poco material escrito en español y para el caso de Ecuador no se ha encontrado material escrito que introduzca a la herramienta. A continuación se describe una discusión sobre los que se ha investigado durante el desarrollo de este proyecto. La evolución en el desarrollo de software no se detiene en el tiempo, y bajo esta premisa se puede plantear dos estrategias, la primera quedarse con una tecnología determinada y ser ineficiente o la segunda evolucionar en el tiempo. Esta última es la óptima, pero claro, la primera es mucho menos costosa. Una aplicación nueva, ahora no se plantea desarrollarla en COBOL, pues lo mismo actualmente se puede aplicar con Java en la mayoría de contextos y próximamente en la totalidad. Según (MONTORO, 2013) dice en su página web, las discusiones sobre que lenguaje de programación es mejor o más productivo suelen ser absurdas porque se basan casi siempre en dos factores que casi nunca importan: La productividad del lenguaje La eficiencia en runtime Si se dice que la productividad de un lenguaje se correlaciona directamente con el número de líneas de código necesarias para escribir un algoritmo. En base a eso, un lenguaje como RoR debería ser mucho más productivo que Java ¿o no? No necesariamente, por dos motivos: primero porque casi ningún programador de Java escribe el código 74 directamente sino mediante asistentes que lo generan; y segundo porque sólo una fracción del código son los algoritmos, habiendo un elevadísimo porcentaje del mismo que son rutinas de control de errores y otras cosas. Es decir, la cantidad de líneas de código necesarias para resolver un problema no es tan relevante como el tiempo real necesario para producirlas y mantenerlas con las herramientas adecuadas. Sobre la eficiencia en runtime se suele argumentar que el precio del ciclo de CPU es ridículamente bajo, mientras que la hora de trabajo de un programador es terriblemente cara. No necesariamente, porque la hora del programador, por cara que resulte, se paga una sola vez y luego se amortiza en forma de un programa que funciona felizmente sin problemas, pero un programa lento y mal hecho nunca deja de ser una caja de sorpresas. Entonces ¿Qué cosas importan realmente en un lenguaje?, (MONTORO, 2013) dice lo siguiente: La disponibilidad de buenas herramientas y librerías. La estabilidad de la plataforma. La garantía de soporte a largo plazo. La disponibilidad de mano de obra competente. La interoperabilidad y compatibilidad con otro software. Es típico ver cómo los usuarios que empiezan algo nuevo desde cero tienen tendencia a usar PHP o Ruby on Rails porque es lo más fácil, mientras que los que ya tienen una infraestructura Java o .NET tienen tendencia a mantenerla. Esto tiene toda la lógica del mundo, una vez que se acostumbra a una plataforma, es muy difícil cambiarla. Cuando se enfrenta por primera vez a código escrito en Ruby, probablemente recuerde otros lenguajes que se haya usado. Esto es a propósito. La mayor parte de la sintaxis es familiar para los usuarios de Perl, Python y Java (entre otros lenguajes), así que si se ha usado alguno de estos, aprender Ruby es relativamente fácil. MVC no es poco común, lo que es raro es la velocidad y la facilidad con la se puede crear un sitio interactivo usando RoR y ActiveRecord. Es por eso que se recomienda que se pruebe la tecnología. Y lo más importante, puede obtener esta velocidad sin sacrificar la calidad del producto final. 75 Se compara ahora dos lenguajes Java y Ruby, el primero posiblemente con afirmaciones muy fuertes sobre su productividad y el segundo incluso no muy conocida y para varios individuos débil y no confiable. Ruby y Java Java está maduro, probado y es rápido. Al ir de Java hacia Ruby, se espera que el tamaño de tu código disminuya considerablemente. También se espera que lleve menos tiempo el armado de prototipos. Se expone ahora algunos pros y contras tanto de Java como de Ruby, según lo que expone (MONTORO, 2013): Java: El entorno J2EE nunca ha acabado de funcionar bien, ni en rendimiento ni en disponibilidad La Comunidad y el soporte de PHP han crecido hasta igualar o superar el de Java, incluyendo grandes fabricantes como IBM, que presta servicios sobre PHP. Java se está debilitando. Con la aparición de nuevos lenguajes: Ruby, Python, Groovy, Scala, entre otros. cada vez se va diluyendo más el uso de Java lo mismo que pasó con el lenguaje C++ a finales de los noventa. Java es relativamente complicado. En particular la rama de EJB y Servicios Web tiene una curva de aprendizaje que crece lentamente. Ruby: Ruby es como el lenguaje de programación que a uno siempre le hubiera gustado diseñar: combina buenas ideas de Perl y Smalltalk, Python, Lisp, Dylany CLU. Ruby ofrece mejor productividad medida en líneas de código necesarias para desarrollar una funcionalidad. Un equipo de programadores bien organizado y motivado puede estar produciendo aplicaciones Ruby on Rails en menos de dos semanas de formación. Lo que muestra que Ruby tiene una curva de aprendizaje de crecimiento rápido. Muchos visionarios de la industria apoyan Ruby, incluyendo al mismísimo James Duncan Davidson, creador de Tomcat, y David Geary, diseñador de JavaServer Faces. 76 Por otra parte también se podría comparar el número de líneas que se utiliza en código de programación por ejemplo para crear el método listar: Código Java Código Ruby Se puede apreciar en este ejemplo simple que Java utiliza varias líneas de código en comparación con las que utiliza Ruby. El resultado obtenido con Ruby on Rails muestra que hay un sin fin de maneras de evitar repetir código (DRY - Don't Repeat Yourself). Se utiliza la arquitectura MVC (Modelo, Vista, Controlador) expuesta en la teoría y se vio que lo que busca es dividir el trabajo de un aplicación en diferentes puntos: el modelo que se encarga de los datos, la vista que se encarga de mostrar los datos de una manera más amigable y el controlador que se encarga de las peticiones y respuestas de información entre el modelo y la vista para que el usuario pueda verla. Se ha visto que las gemas son unas de las grandes novedades que presenta Rails y se pudo ver durante la investigación que existe una comunidad activa desarrollando y mejorándolas, estas ayudan en el desarrollo de aplicaciones, estas pueden ser tan eficientes como Scaffold que es capaz de crear con una sola línea de código el modelo, la vista y el controlador. Rails cumple con los principios de otro modelo, el modelo REST. Sin querer ponerse muy técnico, entre varias cosas esto significa que utiliza las acciones CRUD (Create, Read, Update, Delete) del protocolo HTTP, en código eso se traduce en utilizar POST, GET, PUT Y DELETE como debe hacerse, y en cuestiones de beneficios eso significa que la misma URL puede llamar a las distintas acciones que se menciona. 77 Con la utilización de todas las gemas existentes se puede estar menos preocupado por la programación y más por la visualización y da como resultado páginas más llamativas para el usuario. Con Ruby on Rails el desarrollo de aplicaciones se produce en menos tiempo y con gran flexibilidad pudiendo modificarlos, editarlos o extendiéndolo después de ejecutarlo, lo que conduce a la reducción de costos y una mejor rendimiento en la inversión. Se puede trabajar en una base de datos y luego con toda la facilidad cambiar de gestor sin que esto sea un dolor de cabeza para el desarrollador, en Rails hay un concepto que se llama migraciones que son modificaciones a la base de datos que se guardan en una archivo de esquema, también se puede revertir estas modificaciones así mismo sin que esto presente gran dificultad. 78 5. CONCLUSIONES Al finalizar el presente trabajo se concluye lo siguiente: Conocer la tecnología Ruby on Rails permite establecer con claridad la diferencia de desarrollar aplicaciones web con el lenguaje presentado y otros lenguajes como Java, PHP entre otros. Rails da la libertad de centrarse en otros aspectos, mas no sobre el lenguaje en sí, pues los generadores crean código robusto, seguro y de fácil entendimiento. El estudio de Ruby on Rails abre otro camino para el desarrollo de aplicaciones web, permitiendo que no se estanque en lenguajes de programación tradicionales. La exploración de nuevas herramientas es fundamental para formar un potencial desarrollador. Este proyecto permite determinar con claridad las prestaciones que brinda una aplicación como producto final, y debido a esto se considera los factores más importantes: El patrón MVC, reutilización DRY (Don't Repeat Yourself), la seguridad de aplicación, madurez del producto. Algunos frameworks, como los de Java para aplicaciones web necesitan hacer una múltiple cantidad de configuraciones en archivos y cada una con muchos ajustes, Rails hace esto más fácil asumiendo convenciones sobre configuración que es un paradigma de programación que busca minimizar el número de decisiones que un desarrollador necesita hacer, ganando así en simplicidad pero no perdiendo flexibilidad por ello. La seguridad de la aplicación demanda una correcta utilización de factores que determinan un correcto cuidado de la información tanto al momento de la autenticación de usuarios como al manejo de la aplicación en sí. Ruby on Rails demuestra la eficiencia en la seguridad de las aplicaciones gracias a la utilización de su gema Device, además tiene grandes características de seguridad incorporadas, lo que permite que las aplicaciones web desarrolladas en Rails no se vean afectado por los ataques de inyección SQL. 79 La elaboración de la aplicación web permite entender el manejo de la información estática y dinámica de un sitio, que es fácilmente administrado por el uso de la base de datos o por las páginas HTML. Ruby on Rails es una herramienta robusta, flexible y de fácil manejo, con la cual se verifica que el desarrollo de las aplicaciones no necesita mucho tiempo. Su sintaxis es sencilla, la codificación a largo plazo para cualquier aplicación o página es más fácil de mantener. Ruby on Rails más que un framework es el proyecto central de una comunidad gigante que produce de manera constante librerías (gemas) para simplificar las tareas de crear complejas aplicaciones web. Rails está diseñado con las mejores prácticas, por lo que de manera natural lleva a escribir código increíble. Hay gemas para casi cualquier cosa que se necesita en una aplicación web. Utilizando Rails se puede construir aplicaciones web de lo que se tenga en mente, sólo hay que darle un vistazo a algunos grandes que construyeron sus sitios con Rails: BaseCamp, Airbnb, Github, Hulu, Indiegogo, KickStarter, Pixlr, Shopify, Square, Zendesk entre otros. Hay más de 200,000 sitios web usando Rails. Muchas empresas llamativas que se conoce usan Ruby, como lo son: Amazon, BBC, Cisco, CNET, IBM, JP Morgan, NASA, Yahoo. En pocas horas es posible construir aplicaciones web y dejarlas online funcionando. Para que se tenga una idea, la primera versión de Twitter la hicieron en un día usando Ruby on Rails. Los beneficios de Rails son muchos, y probablemente faltó mencionar el manejo sencillo de relaciones en Rails, el ActiveRecord, migraciones más a detalle, la configuración de los Routers, que con la experiencia del trabajo, son otras razones por las cuales programar en Rails es una buena opción. Ni Java ni PHP desaparecerá en un corto tiempo, ni es óptimo para un desarrollador quedarse utilizando los mismos lenguajes toda una vida. Como reflexión final, se puede expresar que lo mejor de conocer varios frameworks es saber que no existe uno que sea la solución a todos los problemas sino que cada uno fue desarrollado con objetivos diferentes y es necesario ver cuál de todos se alinea mejor con los objetivos de nuestro proyecto evaluando ventajas y desventajas de cada uno. 80 6. RECOMENDACIONES El objetivo del presente proyecto no ha sido solo el de explicar el contexto de Ruby on Rails, si no también transmitir nuevos conceptos y tecnologías que en la actualidad están en pleno desarrollo y en adopción por diversas empresas. Por lo tanto con este trabajo, se recomienda incentivar a docentes y estudiantes la investigación y descubrimiento de nuevas herramientas con las cuales se logre estar un grado por encima del resto en lo que a desarrollo de sistemas se refiere. Es importante que se tenga en mente que para ser un experto en Rails va a conllevar varias horas, así como desarrollar cualquier otra habilidad. Por lo que lo mejor es que se comience utilizando el presente documento tomando en cuenta que es una guía introductoria. Para conocer más sobre los conceptos en Rails es recomendable aprender los conceptos de Ruby que se lo puede empezar por la propia página de Ruby y visitar Tryruby (http://tryruby.org/) donde se puede probar la sintaxis de Ruby y ver cómo funciona. Como desarrollador de aplicaciones web se va a necesitar saber JavaScript, HTML, CSS. No es un pre-requisito para aprender Rails pero si es una habilidad que se necesita aprender. A medida que se sumerge más y más en el mundo web se va a dar cuenta que se necesita saber diferentes tecnologías. Afortunadamente, a medida que se gana experiencia aprender un nuevo lenguaje y framework se hace más fácil. Es necesario constantes actualizaciones de conocimiento de los lenguajes de programación, ya que hoy en día la actualización de los conocimientos es sumamente importante para el crecimiento de la población dedicada al campo de la informática. Los objetivos del análisis se pueden aprovechar en otros ámbitos, como en la enseñanza en aulas universitarias, ya que al estar siempre acostumbrados a programar en base a ciertos parámetros generales, y con un grupo de lenguajes de aplicaciones web, su paradigma de codificación va a estar sujeto a un solo 81 esquema, pero al explorar nuevas herramientas, su manera de solucionar problemas se expande dándole un abanico más amplio de conocimientos y posibles respuestas ante necesidades. Es recomendable conocer las diferentes partes del framework y como trabajar con ellos. Una de las formas más divertidas es con la que ofrece Rails for zombies (http://railsforzombies.org/) un sitio que guia a través de una serie de tutoriales y videos donde se puede ver cómo funciona. Existen numerosas gemas de Ruby para realizar tareas realmente dispares. Y es que el número de éstas ha aumentado considerablemente en los últimos años, proveyendo a los desarrolladores de un gran número de librerías útiles con las que se facilita el trabajo. Se recomienda ver el Anexo A para ver la configuración de dos de estas gemas, Devise y kaminari. Para replicar el ejemplo se debe leer cada una de las secciones completas y luego replicar línea por línea, sin perder de vista cada uno de los detalles que se escriben antes y luego de la presencia del código. Para ver en acción el ejemplo desarrollado que se describe en el proyecto se tiene que descargar el código fuente desde el siguiente link https://www.dropbox.com/s/38rvext96r35mee/WebAppFinal.rar?dl=0, además se puede descargar el ejemplo con mejora en estilos desde el siguiente link https://www.dropbox.com/s/g58yxirys8izhh5/AppWebCEFinal.rar?dl=0, y finalmente cargar el proyecto a su instalacion de Rails de la forma que muestra el Anexo B. 82 BIBLIOGRAFÍA 1. BARZANALLA, R. (2012). Universidad de Murcia. Recuperado el 05 de Enero de 2015, de http://www.um.es/docencia/barzana/DIVULGACION/INFORMATICA/Historiadesarrollo-aplicaciones-web.html 2. BEHAR, D. (2008). Metodología de la investigación. Shalom. 3. BURBECK, S. (2015). How to use Model-View-Controller (MVC). Recuperado el 22 de 02 de 2016, de How to use Model-View-Controller (MVC): http://web.archive.org/web/20150518095937/http://stwww.cs.illinois.edu/users/smarch/st-docs/mvc.html 4. DE LA TORRE VILLAR, E., & NAVARRO DE ANDA, R. (1990). Metodologìa de la Investigación. Mexico: Mcgraw-Hill. 5. DEV, V., & Noria, X. (s.f.). RailsGuide. Obtenido de RailsGuide: http://guides.rubyonrails.org/index.html 6. HEINEMEIER, D. (2006). Ruby on Rails. Recuperado el 23 de 02 de 2016, de Ruby on Rails: http://web.archive.org/web/20131220211233/http://dev.mysql.com/techresources/interviews/david-heinemeier-hansson-rails.html 7. LIBROSWEB.ES. (2006-2016). Intriducción a Ruby on Rails. Recuperado el 05 de Enero de 2016, de http://librosweb.es/libro/introduccion_rails/capitulo_2.html 8. MARK, L. (2010). Learning Python, Fourth edition. O'Reilly. 83 LibrosWeb: 9. MONTORO, S. (2013). Como selecionar una plataforma de desarrolo para un proyecto web. Recuperado el 07 de Enero de 2016, de La pastilla Roja: http://lapastillaroja.net/2013/10/como-seleccionar-plataforma-tecnologica/ 10. ORTIZ, F., & GARCIA, M. d. (2005). Metodología de la Investigación. Mexico: Limusa. 11. PUENTE, R. (2014). Ruby-Gemas, que son y para qué sirven. Recuperado el 06 de Enero de 2016, de Ruby-Gemas, que son y para qué sirven: http://blog.rodrigopuente.com/ruby-gemas-que-son-y-para-que-sirven/ 12. RODRIGUEZ U, M. L. (2013). Acerca de la Investigación Bibliográfica y documental. Recuperado el 04 de Enero de 2016, de Guia de Tesis: https://guiadetesis.wordpress.com/2013/08/19/acerca-de-la-investigacionbibliografica-y-documental/ 13. RUBY, C. (s.f.). Acerca de Ruby. Recuperado el 05 de Enero de 2016, de Ruby: https://www.ruby-lang.org/es/about/ 14. WIKIPEDIA. (2015). Aplicaciones Informaticas. Recuperado el 09 de Enero de 2016, de Wikipedia: https://es.wikipedia.org/wiki/Aplicaci%C3%B3n_inform%C3%A1tica 15. WIKIPEDIA. (2016). Lenguages de Programación. Recuperado el 09 de Enero de 2016, de Wikipedia: https://es.wikipedia.org/wiki/Lenguaje_de_programaci%C3%B3n 16. WIKIPEDIA. (2016). Ruby on Rails. Recuperado el 27 de 02 de 2016, de Ruby on Rails: https://es.wikipedia.org/wiki/Ruby_on_Rails 84 ANEXOS ANEXOS 85 ANEXO A Device Configuración de Vistas Device ayuda a desarrollar rápidamente una aplicación que utiliza autenticación. Sin embargo, no se encuentran los archivos cuando se necesita para personalizarlo. Device es un motor, todas las vistas son empaquetadas dentro de la gema, pero luego de algún tiempo es posible que se desee cambiarlas. Si es el caso, sólo se tiene que invocar el siguiente generador, y se copian todas las vistas a la aplicación: C:\Sites\AppWeb>rails g devise:views Si se desea generar sólo algunas de las vistas, como los módulos de registrar y confirmar, puede pasar una lista de módulos para el generador con la bandera -v. C:\Sites\AppWeb>rails generate devise:views -v registrations confirmations Configuración de Controladores Si la personalización a nivel de vistas no es suficiente, se puede personalizar cada controlador siguiendo estos pasos: Crear sus controladores personalizados utilizando el generador que requiere un scope: C:\Sites\AppWeb>rails generate devise:controllers [scope] Si se especifica Users como scope, los controladores serán creados en app/controllers/usuarios/. Decirle al router para que utilice el controlador, por ejemplo el de sesión: C:\Sites\AppWeb>devise_for :usuarios, controllers: { sessions:"usuarios/sessions" } Copiar las vistas desde devise/sessions to users/sessions. Dado que el controlador se ha cambiado, no va a utilizar las vistas predeterminadas que se encuentran 86 en devise/sessions. Por último, cambiar o ampliar las acciones de los controladores deseados. Kaminari Personalización de la paginación Kaminari incluye un generador de plantilla práctica para editar el paginador. Ejecutar el generador: C:\Sites\AppWeb>rails g kaminari:views default luego editar los parciales de su aplicación en el directorio app/views/kaminari/. Idioma de Etiquetas Las etiquetas por defecto se almacenan el interior de las gemas. Se puede cambiar el valor de las etiquetas para la aplicación adicionando líneas de código en el archivo en.yml que se encuentra en el directorio /config/locales/. En el caso de Devise crea un archivo devise.en.yml en el mismo directorio que se lo puede modificar. También en las aplicaciones que se encuentran para descargar se pude ver más configuraciones de este archivo. Como ejemplo se muestra la configuración de paginación de kaminari en el archivo en.yml: en: hello: "Hola Mundo" views: pagination: first: "&laquo; Primero" last: "Ultimo &raquo;" previous: "&lsaquo; Anterior" next: "Siguiente &rsaquo;" truncate: "..." helpers: page_entries_info: one_page: display_entries: zero: " %{entry_name} No Encontrado" one: "Muestra <b>1</b> %{entry_name}" other: "Muestra <b>Todos los %{count}</b> %{entry_name}" more_pages: display_entries: "<b>&nbsp; %{first}&nbsp; al &nbsp;%{last}</b>&nbsp; de &nbsp; <b>%{total}</b>&nbsp;" 87 ANEXO B Cargar aplicación al servidor Para cargar la aplicación que se dejó en los link de la sección recomendaciones al servidor de Rails, se tiene instalar Rails si aún no se lo ha hecho como se muestra en el punto número 3 de este documento y luego descomprimir el archivo .rar y copiar la carpeta resultante al directorio C:\Sites\. Luego mediante el terminal ingresar a la carpeta del proyecto con el comando cd nombreProyecto, así. C:\Sites\>cd AppWeb Se verifica la instalacion de gemas con los comandos bundle install y bundle update respectivamente, así C:\Sites\AppWeb> bundle install C:\Sites\AppWeb> bundle update Se crea la base de datos desde la aplicación, tomar en cuenta que se debe cambiar los datos del archivo /config/database.yml con los de su base de datos, y luego ejecutar: C:\Sites\AppWeb>rake db:create Se carga el esquema de los modelos, con el comando: C:\Sites\AppWeb>rake db:schema:load Finalmente se inicia el servidor C:\Sites\AppWeb>rails s 88 ANEXO C Configurar Correo Electrónico Para establecer el sistema de correo electrónico para la aplicación con devise y que se pueda enviar correos electrónicos, para restablecer contraseñas. Se debe editar varios archivos. Primero abrir el archivo devise.rb que se encuentra en el directorio /config/initializer y realizar el cambio de: config.mailer_sender = '[email protected]' por el email que se quiere que envié los correos: config.mailer_sender = '[email protected]' además se debe verificar que la siguiente línea este descomentada. config.secret_key = 'c7167fda544135cdc6eaa8124d9ea3944dbeb29274c79b8e045103a18e3ef5e5b b1a849ae9873b1ae158560aeb8666d03d301b2290561abac11db00e89351e2d' Los archivos que se pueden editar se encuentran en el directorio /config/environments/, este contiene los archivos para configurar los entornos de pruebas, desarrollo o producción, en este caso se configura el archivo de desarrollo development.rb, agregando las líneas de código se configura el envió de email con una cuenta de outlook: config.action_mailer.default_url_options = { :host => 'localhost:3000' } config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: "smtp.live.com", port: 587, authentication: "plain", enable_starttls_auto: true, user_name: "[email protected]", password: "tupassword" } 89 Ahora ya se puede restablecer contraseñas desde la autenticación de device, esta configuración se debe realizar al entorno que se desea. Para poder cambiar la configuración con otros servidores de email se muestra características de algunos de estos: YAHOO: • Servidor de correo • Servidor de correo • Port: 587 GMAIL: • Servidor de correo • Servidor de correo • Port: 587 HOTMAIL y OUTLOOK: • Servidor de correo • Servidor de correo • Port: 587 entrante (POP3): pop.correo.yahoo.es saliente (SMTP): smtp.correo.yahoo.es entrante (POP3): pop.gmail.com saliente (SMTP): smtp.gmail.com entrante (POP3): pop3.live.com saliente (SMTP): smtp.live.com 90 ANEXO D Carrito Básico de Compras En este punto se va a manejar sesiones y poder crear un carrito básico de compras, para ello primero se añade al método index() del controlador de home la lista de los productos que se tiene de la siguiente manera: class HomeController < ApplicationController def index @productos = Producto.order(:nombre).page(params[:page]).per(10) end end se los visualiza añadiendo a la vista index.html.erb de home el siguiente código: <div class="row center-xs"> <div class="col-sm-8"><h1>Lista de Productos</h1> <table class="table table-bordered table-striped"> <thead> <tr> <th>Nombre</th> <th>Descripcion</th> <th>Precio</th> <th>Fecha</th> <th>Tipo producto</th> <th>Fabricante</th> <th>Imagen</th> <th colspan="3"></th> </tr> </thead> <tbody> <% @productos.each do |producto| %> <tr> <td><%= producto.nombre %></td> <td><%= simple_format(h(producto.descripcion)) %></td> <td><%= number_to_currency(producto.precio) %></td> <td><%= producto.fecha %></td> <td><%= producto.tipo_producto.nombre %></td> <td><%= producto.fabricante.nombreFab %></td> <td><%= image_tag producto.urlImagen.url() %></td> </tr> <% end %> </tbody> </table> <%= paginate @productos %> </div></div> 91 Figura 31 Producto Home A medida que navegan por la aplicación, se espera seleccionar los productos a comprar. La convención es que cada elemento seleccionado será añadido a un carrito de compra virtual. Esto significa que la aplicación tendrá que mantener un registro de todos los productos añadidos al carrito. Para realizarlo, se va a crear un carrito en la base de datos y almacenar su identificador único, cart.id, en la sesión. Cada vez que llega una petición, se puede recuperar la identidad de la sesión y utilizarlo para encontrar el carrito en la base de datos. Para ello crear el modelo Cart. C:\Sites\AppWeb>rails generate scaffold Cart luego migrarlo como se lo ha estado haciendo C:\Sites\AppWeb>rake db:migrate Rails hace que se mire a las sesiones presentes como un hash para el controlador, por lo que se va a almacenar el ID del carrito en la sesión mediante la indexación con el símbolo :cart_id. Para esto se crea el archivo current_cart.rb en el directorio /app/controllers/concerns/ con el siguiente contenido. 92 module CurrentCart extend ActiveSupport::Concern private def set_cart @cart = Cart.find(session[:cart_id]) rescue ActiveRecord::RecordNotFound @cart = Cart.create session[:cart_id] = @cart.id end end El método set_cart() inicia por obtener el :cart_id del objeto de sesión y luego intenta encontrar un carrito correspondiente a este ID. Si no se encuentra un registro, entonces este método va a proceder a crear un carrito nuevo, almacena el ID del carrito creado en la sesión, y luego devuelve el carrito nuevo. Un carrito contiene un conjunto de productos. Ahora generar el modelo para crear la conexión entre el carrito y los productos de la siguiente forma. C:\Sites\AppWeb>rails generate scaffold LineItem producto:references cart:belongs_to luego migrarlo como se lo ha estado haciendo rake db:migrate La base de datos tiene ahora un lugar para almacenar las referencias entre artículos de sesión, carritos y productos. Si se observa en la definición de la clase generada LineItem, se puede ver las definiciones de estas relaciones. class LineItem < ActiveRecord::Base belongs_to :product belongs_to :cart end Para ser capaces de atravesar estas relaciones en ambas direcciones, se tiene que añadir algunas declaraciones de nuestros archivos de modelos que especifican sus relaciones inversas. Primero se lo hará para el modelo Cart. class Cart < ActiveRecord::Base has_many :line_items, dependent: :destroy end La parte de la directiva has_many: line_items es bastante explica por sí mismo: un carrito (potencialmente) tiene muchos elementos asociados con line_items. Estos 93 están vinculados al carrito porque cada line_items contiene una referencia a la ID del carrito. La parte dependent: :destroy indica que la existencia de un line_items depende de la existencia del carrito. Si se destruye un carrito, eliminarlo de la base de datos, rails también destruye los line_items que se asocian con ese carrito. Ahora, se tiene que añadir una directiva has_many al modelo del Producto. has_many :line_items Después de todo, si se tiene varios carritos, cada producto puede tener varios line items que hacen referencia a ellos. Esta vez, se va a hacer uso de un código de validación para impedir remover los productos que son referenciados por los line items. En el mismo modelo de Producto aumentar el callback y el método siguiente. before_destroy :ensure_not_referenced_by_any_line_item #....... private # asegura de que no hay ninguna line_item que hacen referencia a un producto def ensure_not_referenced_by_any_line_item if line_items.empty? return true else errors.add(:base, 'Line Items presente') return false end end Aquí se declara que un Producto tiene muchos Line Items y se define un método hook llamado ensure_not_referenced_by_any_line_item(). Un método hook es un método que Rails llama automáticamente en un punto dado en la vida de un objeto. En este caso, se llama al método antes que Rails intente destruir una fila en la base de datos. Si el método hook devuelve false, la fila no será destruida. Es el momento de colocar un botón “Añadir al Carrito” para cada producto. No hay necesidad de crear un nuevo controlador o incluso una nueva acción. Lo que se está creando ciertamente no es un carro o incluso un producto. Se está creando un LineItem. En total, la línea que hay que añadir a la vista index.html.erb del modelo home se ve así: 94 <td><%= button_to 'Añadir al carrito', line_items_path(producto_id: producto) %></td> Quedando de la siguiente forma <div class="row center-xs"> <div class="col-sm-8"> <h1>Lista de Productos</h1> <table class="table table-bordered table-striped"> <thead> <tr> <th>Nombre</th> <th>Descripcion</th> <th>Precio</th> <th>Fecha</th> <th>Tipo producto</th> <th>Fabricante</th> <th>Imagen</th> <th colspan="3"></th> </tr> </thead> <tbody> <% @productos.each do |producto| %> <tr> <td><%= producto.nombre %></td> <td><%= simple_format(h(producto.descripcion)) %></td> <td><%= number_to_currency(producto.precio) %></td> <td><%= producto.fecha %></td> <td><%= producto.tipo_producto.nombre %></td> <td><%= producto.fabricante.nombreFab %></td> <td><%= image_tag producto.urlImagen.url() %></td> <td><%= button_to 'Añadir al carrito', line_items_path(producto_id: producto) %></td> </tr> <% end %> </tbody> </table> <%= paginate @productos %> </div> </div> 95 Figura 32 Añadir Carrito Ahora se modificar el controlador LineItemsController para encontrar el carrito para la sesión actual (la creación de uno si no hay uno ya), añadir el producto seleccionado para ese carrito, y mostrar el contenido del carrito. Se incluye el módulo CurrentCart que se implementó para encontrar (o crear) un carrito en la sesión. Para esto se aumenta las líneas en el controlador app/controllers/line_items_controller.er class LineItemsController < ApplicationController include CurrentCart before_action :set_cart, only: [:create] before_action :set_line_item, only: [:show, :edit, :update, :destroy] # GET /line_items ………. end Se Incluyó el módulo CurrentCart y declara que el método set_cart() es llamado antes de la acción create(). 96 Asociar un contador con cada producto en nuestro carrito va a requerir modificar el modelo line_items. Utilizando las migraciones se va aumentar un campo cantidad así: C:\Sites\AppWeb>rails generate migration add_cantidad_to_line_items cantidad:integer Antes de migrar se va a modificar el archivo para aumentar el valor default: 1 a la migración quedando de la siguiente manera class AddCantidadToLineItems < ActiveRecord::Migration def change add_column :line_items, :cantidad, :integer, default: 1 end end Una vez completado, se corre la migración con rake db:migrate Ahora se necesita un método inteligente en nuestro carrito add_product(), que comprueba si en los line_items ya incluye el producto que se está añadiendo; Si lo hace, se aumenta en la cantidad, y si no lo hace, se construye un nuevo Line Item. Esto método se lo crea en el modelo Cart, observándose de la siguiente manera: class Cart < ActiveRecord::Base has_many :line_items, dependent: :destroy def add_product(producto_id) current_item = line_items.find_by(producto_id: producto_id) if current_item current_item.cantidad += 1 else current_item = line_items.build(producto_id: producto_id) end current_item end end El método find_by() es una versión simplificada del método where(). En lugar de devolver un conjunto de resultados, se devuelve un Line Item existente o nulo. También se tiene que modificar el controlador LineItem para utilizarlo, modificar el método create() del controlador así: 97 def create producto = Producto.find(params[:producto_id]) @line_item = @cart.add_product(producto.id) respond_to do |format| if @line_item.save format.html { redirect_to @line_item.cart, notice: 'Producto Añadido al Carrito' } format.json { render action: 'show', status: :created, location: @line_item } else format.html { render action: 'new' } format.json { render json: @line_item.errors, status: :unprocessable_entity } end end end Lo que es probable que se ve es una mezcla de productos individuales enumerados por separado y un solo producto que aparece con cantidad de dos. Esto se debe a que se ha añadido una cantidad de una de las columnas existentes en lugar de colapsar varias filas cuando sea posible. Para eso se usara una migración. C:\Sites\AppWeb>rails generate migration combinar_items_en_carrito En esta ocasión, rails no puede inferir lo que se está tratando de hacer, por lo que no se puede apoyarse en la método generado change(). Lo que se hace en su lugar es reemplazar este método con un método separador up(). Esto es fácilmente el código más extenso que se ha visto hasta ahora. Empezar por la iteración en cada carrito. • Para cada carrito, se obtiene una suma de los campos de cantidad para cada uno de los elementos asociados a este carrito, se agrupa por producto_id. Las sumas resultantes serán una lista de pares ordenados de producto_ids y cantidad. • Se Itera sobre estas sumas, extrayendo el producto_id y la cantidad de cada uno. • En los casos en que la cantidad es mayor que uno, se va a eliminar todos los line items individuales asociados a este carrito y el producto y reemplazarlo con un único line item con la cantidad correcta. 98 Para esto se modifica la migración así: class CombinarItemsEnCarrito < ActiveRecord::Migration def up # Sustituye varios items para un solo producto en un carrito con un solo item Cart.all.each do |cart| # cuenta el número de cada producto en el carrito sums = cart.line_items.group(:producto_id).sum(:cantidad) sums.each do |producto_id, cantidad| if cantidad > 1 # elimina elementos individuales cart.line_items.where(producto_id: producto_id).delete_all # sustituye por un solo elemento item = cart.line_items.build(producto_id: producto_id) item.cantidad = cantidad item.save! end end end end end Tenga en cuenta la facilidad y elegancia que Rails le permite expresar este algoritmo. Con este código en su lugar, se aplica la migración al igual que cualquier otra migración. C:\Sites\AppWeb>rake db:migrate Si el carro no se puede encontrar, Active Record lanza una excepción RecordNotFound, que claramente se tiene que manejar. Surge la pregunta ¿cómo? Para esto se va a volver a mostrar la página index de home junto con un breve mensaje (algo como "carrito Inválido") para que pueda seguir utilizando la aplicación. Rails tiene una forma conveniente de hacer frente a los errores y los informes de errores. Se define una estructura llamada un flash. Normalmente, el flash se utiliza para recoger los mensajes de error. Armado con estos antecedentes acerca de los datos de flash, ahora se puede crear un método invalid_cart () que informe sobre el problema. Se aumenta lo siguiente rescue_from y el método privado invalid_cart() en el controlador de Carts asi. 99 class CartsController < ApplicationController before_action :set_cart, only: [:show, :edit, :update, :destroy] rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart # GET /carts ……. Private ….. def invalid_cart logger.error " Intenta acceder al carrito no válido #{params[:id]}" redirect_to root_path, notice: ' Carrito Invalid' end end Se debe de preocupar por cada interfaz posible porque las crackers maliciosos pueden conseguir por debajo de HTML que se proporciona y tratar de proporcionar parámetros adicionales. Los Carritos inválidos no son nuestro mayor problema aquí; también se quiere impedir el acceso a los carritos de otras personas. Como siempre, los controladores son la primera línea de defensa. Se va a retire el parámetro :cart_id de la lista de parámetros que están permitidos en el controlador de Line Item, quedando de la siguiente manera. def line_item_params params.require(:line_item).permit(:producto_id) end Ahora en el controlador de Cart, se va a modificar el método destroy() para garantizar que el usuario está eliminando su propio carrito y para quitar el carrito de la sesión antes de redirigir a la página de inicio con un mensaje de notificación. def destroy @cart.destroy if @cart.id == session[:cart_id] session[:cart_id] = nil respond_to do |format| format.html { redirect_to root_path, notice: ' Su carrito está vacío ' } format.json { head :no_content } end end y también se modifica el método index() del mismo archivo quedando así: 100 def index @carts = Cart.all respond_to do |format| format.html {redirect_to root_path} end end Para finalizar y poder ver el carrito en acción se tiene que editar la vista show del modelo Carts colocando el siguiente código <% if notice %> <p id="notice"><%= notice %></p> <% end %> <h2>Mis Compras</h2> <table class="table table-bordered table-striped"> <thead> <tr> <th></th> <th>Cantidad</th> <th>Producto</th> <th>Precio</th> <th>Total</th> </tr> </thead> <tbody> <% @cart.line_items.each do |item| %> <tr> <td><%= image_tag item.producto.urlImagen.url() %></td> <td><%= item.cantidad %>&times;</td> <td><%= item.producto.nombre %></td> <td class="item_price"><%= number_to_currency(item.producto.precio) %></td> <% total = item.cantidad*item.producto.precio %> <td><%= number_to_currency(total) %></td> </tr> <% end %> </tbody> </table> <%= button_to 'Eliminar Carrito', @cart, method: :delete,data: { confirm: 'Estás Seguro?' } %> 101 Figura 33 Mis compras 102 ANEXO E Diagrama de flujo El presente punto de diseño de la aplicación web nace el entendimiento detallado de los requisitos y requerimientos que se desea. Este documento organiza la información obtenida y la analiza para conformar los diagramas que servirá al desarrollo. Análisis y Especificación de requerimientos de software Requerimientos funcionales y no funcionales del sistema Los requerimientos funcionales sirven para definir lo que el sistema será capaz de hacer. En base al análisis realizado se han identificado los siguientes requerimientos: Requerimientos funcionales El sistema permitirá la gestión (crear, modificar, deshabilitar) de Tipos Productos. El sistema permitirá la gestión (crear, modificar, deshabilitar) de Fabricantes. El sistema permitirá la gestión (crear, modificar, deshabilitar) de Proveedores. El sistema permitirá la gestión (crear, modificar, deshabilitar) de Productos. Requerimientos No Funcionales El sistema permitirá gestionar los usuarios (crear, modificar, deshabilitar). El sistema permitirá la autenticación de los usuarios por usuario y contraseña. El sistema permitirá cambiar la contraseña al usuario. El sistema permitirá la creación de un carrito de compras. El sistema debe ser web. El sistema será soportado en navegadores Firefox, Safari, Opera, Chrome y/o Explorer. Modelo de Dominio La elaboración del modelo de dominio consiste en la construcción del glosario del proyecto, o un diccionario de términos que serán usados en el proyecto. Este modelo es la fuente inicial sobre la cual se describirán los casos de uso mostrando gráficamente 103 como se relacionan los diferentes objetos identificados durante la etapa de análisis de requerimientos para el sistema. Para el desarrollo del sistema se ha elaborado el modelo de dominio representado en la Figura 13 de la sección 3. Este modelo de dominio contiene los objetos básicos que han sido identificados. Los objetos más representativos que se han identificado son Tipo de Producto, Fabricante, Producto y Proveedor. Casos de Uso Para la elaboración e identificación de los diagramas de casos de uso se ha partido del análisis de los requisitos del sistema. Se ha identificado claramente al actor (Súper usuario), el cuales nos permitirán analizar de forma distribuida el Casos de Uso para este Actor. Casos de uso actor Súper Usuario El Súper usuario es el Administrador del Sistema que tendrá acceso a cada parte del sistema. El diagrama de casos de uso se lo puede ver en la Figura 1.2. Figura 34 Super Usuario Descripción de Casos de Uso A continuación se describirán los casos de uso, además se realizará el diagrama de flujo correspondiente. 104 Figura 35 Un diagrama de flujo a nivel de contexto 105 Nombre Caso de Uso Descripción Actor Precondición Postcondición Flujos principal alternativo(s) Ingresar Al Sistema El usuario podrá ingresar al sistema. Usuarios Usuario debe estar registrado y poseer un nombre de usuario, contraseña Usuario accede al sistema. y Nota 106 Nombre Caso de Uso Descripción Actor Precondición Postcondición Flujos principal y alternativo(s) Registrar Usuario El usuario puede registrar su cuenta en sistema. Súper Usuario El usuario del sistema desea registrarse. Se crea una cuenta de usuario Nota 107 Nombre Caso de Uso Descripción Actor Precondición Postcondición Flujos principal y alternativo(s) Actualizar Mi Información El usuario podrá actualizar su información. Súper Usuario Usuario ha iniciado sesión Usuario ha actualizado su información. Nota 108 Nombre Caso de Uso Descripción Actor Precondición Postcondición Flujos principal alternativo(s) Registrar Modelo (Tipo, Fabricante, Proveedor y Producto) El súper usuario del sistema se encargara de crear los modelos del sistema. Súper Usuario Usuario ha iniciado sesión y existen modelos por crear. Se crea un modelo y Nota 109 Nombre Caso de Uso Descripción Actor Precondición Postcondición Flujos principal alternativo(s) Consultar Modelo (Tipo, Fabricante, Proveedor y Producto) El usuario puede consultar los modelos y visualizar información de cada uno de ellos. Súper Usuario Usuario ha iniciado sesión. El sistema despliega información del modelo que se ha seleccionado. y Nota 110 Nombre Caso de Uso Descripción Actor Precondición Postcondición Flujos principal alternativo(s) Actualizar Modelo (Tipo, Fabricante, Proveedor y Producto) El usuario podrá actualizar la información de un modelo seleccionado. Súper Usuario Usuario ha iniciado sesión. Usuario habrá modificado los datos de un modelo registrado. y Nota 111