• No se han encontrado resultados

Restful Objects para Ruby, un framework para el desarrollo de Web Services RESTful

N/A
N/A
Protected

Academic year: 2020

Share "Restful Objects para Ruby, un framework para el desarrollo de Web Services RESTful"

Copied!
87
0
0

Texto completo

(1)

Universidad Nacional del Centro de la Provincia De Buenos Aires

Facultad de Ciencias Exactas - Departamento de Computación y Sistemas

Ingeniería de Sistemas

Restful Objects para Ruby, un framework para el desarrollo

de Web Services RESTful

por

Pablo Daniel Vizcay

Director: Dr. Alejandro Zunino

(2)

Resumen

El presente informe introduce el framework RestfulObjects para Ruby: las motivaciones para su creación, el diseño y los detalles más relevantes de la implementación del mismo. Como introducción se realiza un breve análisis del funcionamiento del protocolo HTTP y las distintas arquitecturas de Web Services resaltando las características de un servicio RESTful. También se realiza un recorrido por la evolución de distintas tecnologías que culminaron en la creación del estándar RestfulObjects, el cual implementa el framework RestfulObjects para Ruby propuesto en este trabajo.

Luego, se realiza una introducción a como se interactúa con una instanciación del framework, desde distintas herramientas analizando las interacciones sobre HTTP: URLs, métodos, encabezados y cuerpos de mensaje en JSON (JavaScript Object Notation, un formato de intercambio de datos muy utilizado en Internet). Se realiza un análisis de los distintos elementos relevantes: interacción con objetos modelos del dominio, lectura y modificación de propiedades, incorporación y exclusión de objetos en colecciones, ejecución de acciones y creación de objetos entre otros. También se ejemplifica el funcionamiento del sistema de descripción de tipos del estándar.

Posteriormente, se realiza un extenso análisis del diseño del framework: los componentes que lo integran, los requerimientos funcionales y no funcionales, y clases relevantes con diagramas UML y extractos de códigos necesarios para un mejor entendimiento de las mismas. Se expone porque Ruby es un lenguaje que, con sus características de meta programación, es idóneo para la implementación de esta clase de frameworks. Por último se explica la estructura de la arquitectura de testing automático con el que cuenta.

Sobre el final del informe se analiza el funcionamiento de una aplicación de gestión de proyectos y tareas que instancia el framework en forma de ejemplo: su interacción con el backend, interfaz de usuario y demás por menores.

(3)

Tabla de contenidos

Resumen...2

Tabla de contenidos...3

Índice de figuras... 6

Glosario... 7

Capítulo 1 – Introducción...8

1.1 Motivación...8

1.2 Restful Objects para Ruby...9

1.3 Organización...9

Capítulo 2 – Contexto... 12

2.1 Introducción...12

2.2 Naked Objects... 12

2.2.1 Naked Objects y la Web... 13

2.3 Domain Driven Design...13

2.4 El protocolo HTTP... 14

2.4.1 Una interfaz uniforme mediante métodos HTTP... 14

2.4.2 Direccionamiento de los recursos... 14

2.4.3 Encabezados HTTP... 15

2.4.4 Cuerpo de la entidad HTTP...15

2.4.5 Códigos de respuesta HTTP estandarizados... 16

2.5 Análisis de los distintos tipos de servicios Web... 16

2.5.1 Arquitecturas tipo RPC (Remote Procedure Call)... 16

2.5.2 Arquitecturas RESTful y Resource-Oriented... 16

2.5.3 Arquitecturas híbridas REST-RCP... 17

2.6 RESTful versus SOAP y WSDL... 17

2.7 Ruby: un lenguaje dinámico...18

Capítulo 3 – La especificación Restful Objects... 19

3.1 Introducción...19

3.2 Objetivos... 19

3.3 Interfaz uniforme...20

3.4 Recursos y representaciones...20

3.5 Identificación del “Content-type” estandarizado... 21

3.6 Valores de datos soportados y formatos... 23

3.7 Formato de referencias a otros objetos...24

3.8 Objetos del dominio... 24

3.8.1 Ejemplos de solicitudes HTTP interactuando con objetos del dominio... 25

3.9 Servicios del dominio...26

3.10 Apache ISIS...27

3.11 Restful Objects for .Net... 27

3.13 Clientes genéricos versus específicos...28

3.13.1 Clientes genéricos... 28

3.13.2 Clientes específicos... 28

(4)

4.2 Ordenamiento topológico... 29

4.3 La implementación en Restful Objects... 31

4.4 Iniciar el Web Service...33

4.5 Crear un objeto de dominio... 33

4.6 Leer y escribir propiedades... 36

4.7 Agregar un elemento a una colección...37

4.8 Ejecutar una acción... 38

4.9 Interactuando con el Web Service desde la terminal...40

4.9.1 Interacciones HTTP con “curl”... 40

4.9.2 Formateo y manipulación de JSON con “jq”... 40

4.10 Consultando los meta datos de las clases... 40

4.11 Una interacción completa mediante un script RUBY... 42

4.11.1 Análisis del log de la interacción... 44

4.12 Una interacción completa mediante un script BASH...44

4.12.1 Análisis del script... 45

Capítulo 5 – Diseño e implementación del framework... 47

5.1 Introducción...47

5.2 Requerimientos del framework... 47

5.3 Estrategia de modularización del framework...48

5.3.1 Vista UML de componentes general... 48

5.3.2 Módulos Ruby para organizar el código... 49

5.4 Utilización de la meta programación en Ruby... 50

5.4.2 Módulos para realizar mixins... 50

5.4.2 Meta programación con “class macros”... 51

5.5 El subcomponente RestfulObjects::DomainModel... 54

5.5.1 La clase RestfulObjects::DomainModel::DomainModel...54

5.5.2 El módulo RestfulObjects::Object... 55

5.5.3 El módulo RestfulObjects::ObjectBase... 57

5.5.4 El módulo RestfulObjects::ObjectProperties...58

5.5.5 El módulo RestfulObjects::ObjectActions... 59

5.5.6 El módulo RestfulObjects::ObjectCollections...60

5.5.7 El módulo RestfulObjects::Service... 60

5.6 El subcomponente RestfulObjects::Router... 61

5.6.1 El módulo RestfulObjects::Router::DomainObjectResources... 62

5.7 Diagrama de secuencia: obtener representación de un objeto...64

5.8 Testing del framework con RSpec...65

5.8.1 Ejemplo de unit test: object_properties_spec.rb...65

5.8.2 Ejemplo de integration test: objects_collections_spec.rb... 66

Capítulo 6 – Una instanciación del framework... 68

6.1 Introducción...68

6.2 TodoApp: un sencillo gestor de proyectos... 68

6.3 Diseño del backend: aplicación Ruby con RestfulObjects...68

6.4 Diseño del frontend: una SPA con Backbone.js... 69

6.4.1 Backbone.js... 69

6.4.2 Twitter Bootstrap... 70

6.4.3 Coffescript, Haml & Sass... 70

6.4.4 Underscore & JQuery... 70

6.5 Funcionamiento del frontend...71

6.5.1 El tablero de información... 71

6.5.2 La lista de proyectos... 72

6.5.3 El editor de proyectos... 73

(5)

6.5.5 El editor de tareas y recursos... 74

6.5.4 El visor de logs... 75

Capítulo 7 – Distribución y documentación de sintaxis...77

7.1 Introducción...77

7.2 Empaquetado, distribución e instalación...77

7.2.1 Procedimiento de empaquetado y publicación... 78

7.2.2 Procedimiento de descarga e instalación global... 79

7.3 Sintaxis para la declaración de objetos del dominio... 80

7.3.1 Declaración de Objetos del Dominio... 80

7.3.2 Declaración de Propiedades... 80

7.3.3 Declaración de Acciones o Métodos... 81

7.3.4 Declaración de Colecciones... 82

7.3.5 Declaración de Servicios... 83

Capítulo 8 – Conclusiones... 84

Limitaciones... 84

Bibliografía... 86

Papers y tesis académicas...86

Libros...86

(6)

Índice de figuras

Figura 1: Diagrama de recursos, representaciones y métodos HTTP para RestfulObjects...21

Figura 2: Interfaz gráfica autogenerada por Apache ISIS... 27

Figura 3: Interacción entre curl y el framework RestfulObjects...36

Figura 4: Diagrama de componentes UML del framework... 47

Figura 5: Diagrama UML de clases estático del framework...53

Figura 6: Diagrama de clases de RestfulObjects::ObjectBase... 56

Figura 7: Diagrama de clases de RestfulObjects::ObjectProperties...57

Figura 8: Diagrama de clases de RestfulObjects::ObjectActions... 58

Figura 9: Diagrama de clases de RestfulObjects::ObjectCollections...59

Figura 10: Diagrama con las arquitectura general de las rutas... 60

Figura 11: Patrón de diseño “Message Router”... 61

Figura 12: Diagrama UML de secuencia de una interacción... 63

Figura 13: Diagrama de componentes general...68

Figura 14: Captura de pantalla del tablero de control de TodoApp... 71

Figura 15: Captura de pantalla de la lista de proyectos de TodoApp...71

Figura 16: Captura de pantalla del editor de proyectos de TodoApp...72

Figura 17: Captura de pantalla de la lista de tareas de TodoApp... 73

Figura 18: Captura de pantalla del editor de tareas de TodoApp... 74

Figura 19: Captura de pantalla del visor de logs de TodoApp... 75

(7)

Glosario

API: Application Programming Interface es una especificación de procedimientos o funciones, protocolos y mecanismos para interactuar con una pieza de software.

Backend: respecto a una aplicación WEB, se refiere a la parte de la misma que corre en el servidor y provee la lógica de negocios, base de datos y demás procesos centrales relevantes.

DDD: Domain Driven Design es una metodología de realizar el desarrollo de software complejo creada por Eric Evans.

DSL: Domain Specific Language es un lenguaje de computadoras especializado en un dominio en particular.

Frontend: respecto a una aplicación WEB, se refiere a la parte de la misma que corre en los dispositivos del cliente, proveyendo una interfaz de usuario para comunicarse con el backend.

HATEOS: Hypermedia As The Engine Of Application State es un requerimiento de una arquitectura RESTful respecto a la utilización de links para interactuar y navegar los estados del Web Service.

JSON: Javascript Object Notation es un estándar abierto de comunicación, leblee por las personas, que utiliza texto para transmitir objectos con una notación atributo valor.

RESTful: es un conjunto de requerimientos que tiene que cumplir un Web Service respecto a su diseño para contar con determinadas propiedades que son de interés.

SOAP: Simple Object Access Protocol es una especificación para el intercambio de información estructurada a través de un Web Service.

Web Service: es un servicio ofrecido por un dispositivo electrónico, diseñado para ser consumido por dispositivo a través de Internet.

(8)

Capítulo 1 – Introducción

1.1 Motivación

En los últimos años se produjo un gran avance en el desarrollo de aplicaciones distribuidas que se comunican mediante Web Services, lo que ha permitido que las mismas interactúen y se complementen en Internet de forma nunca antes pensada (Webber et al., 2010). El protocolo principal elegido como medio de transporte, por ser omnipresente en Internet, es HTTP. El mismo cuenta con

implementaciones robustas, está ampliamente extendido y generalmente no presenta problemas de conexión con la mayoría de los firewalls (Gourley et al., 2002).

Sin embargo, en vez de utilizar dicho protocolo de mensajes de forma nativa para implementar dichos Web Services, se lo ha utilizado como un medio de transporte para otros dos protocolos: SOAP (Simple Object Access Protocol) y WSDL (Web Services Description Language). La simplicidad de SOAP, contrasta con la complejidad que puede implicar las especificaciones generadas con WSDL. Excepto en los casos más sencillos, solamente es práctico producir y consumir interfaces WSDL por herramientas para el desarrollo de aplicaciones que generan código automáticamente (Richardson & Ruby, 2007).

Los desarrolladores de software comenzaron a desarrollar toda clase de Web Services con dichas tecnologías. Sin embargo, la complejidad agregada tenía un costo importante: problemas de

compatibilidad por la interoperabilidad de distintos stacks tecnológicos que supuestamente cumplían el mismo standard, problemas para el diseño de las interfaces, gran costo asociado a la depuración de errores y grandes problemas de escalabilidad junto a un alto requerimiento en la transferencia de datos (Pautasso, 2008) (Zunino, 2012).

Debido a estos motivos, recientemente se produjo un cambio de eje buscando nuevamente la simplicidad. Los desarrolladores de software han incrementado su interés en la implementación de aplicaciones consideradas RESTful (Fielding, 2000). Una arquitectura RESTful es simplemente un estilo arquitectónico que adhiere al uso del protocolo HTTP como fue realmente diseñado,

• basado en recursos y representaciones de los mismos

• utilización de URIs (Uniform Resource Identifier) como direcciones

(9)

semántica de los mismos

• dirigido por el hipertexto, generado en las mismas representaciones que se transfieren.

Cuando un servicio cumple el último requerimiento, se dice que está basado en HATEOS (Hypermedia as the Engine of Application State) (Richardson, 2007). RESTful no obliga a utilizar una determinada representación para los mensajes en sí, pero JSON y XML son las elecciones más habituales.

Utilizar principios RESTful para el desarrollo de Web Services conlleva además de la simplicidad de las interfaces y la navegación de las mismas por hyperlinks, grandes ventajas en la escalabilidad al poder utilizar el caching nativo de HTTP de forma mucho más agresiva (Webber & Robinson, 2010). Pero, al ser un estilo arquitectónico y no un protocolo como SOAP+WSDL, RESTful deja mucha libertad al desarrollador sobre cómo exponer un modelo de objetos que implementen los servicios, que a fin de cuentas, es el interés principal de los implementadores.

Este vacío es el que intenta llenar Restful Objects (Haywood, 2012), una especificación pública y abierta que adhiere a los principios RESTful, que define un conjunto de rutas generales y cuerpos de mensaje en formato JSON para exponer de forma genérica cualquier dominio de modelos de objetos. Actualmente no existen muchas herramientas para facilitarle al desarrollador la implementación del mismo, existiendo sólo dos frameworks para servidores que lo implementan: Apache Isis en Java [http://isis.apache.org/] y Restful Objects for .NET [http://restfulobjects.codeplex.com].

1.2 Restful Objects para Ruby

El objetivo del trabajo final fue el de desarrollar un framework en el lenguaje Ruby que facilite la implementación de Web Services que cumplan con el standard Restful Objects. El mismo debe abstraer los detalles de implementación al programador, tales como el funcionamiento del protocolo sobre HTTP o la generación de JSON, y permitirle concentrarse en el diseño del modelo de objetos que implemente la lógica de negocios que es su verdadero interés.

1.3 Organización

Este informe está organizado de la siguiente manera:

(10)

(DDD), los estilos arquitectónicos más comunes para implementar Web Services, cuál es el criterio para considerar como RESTful a un servicio, su relación con el protocolo HTTP y una breve introducción a las características de Ruby como lenguaje dinámico.

• Capítulo 3 - La especificación Restful Objects: realizaremos un análisis del estándar para poder comprender la implementación del framework: objetivos, interfaz o API, diferencias entre recursos y representaciones, encabezados HTTP relevantes, objetos y servicios de dominio y algunos detalles de la especificación relevantes como el formato del JSON usado como medio de transporte. Por último, se hará un breve análisis de otros dos frameworks para el servidor que cumplen con el estándar: Apache ISIS y Restful Objects for .Net.

• Capítulo 4, - Introducción a Restful Objects para Ruby: será un tutorial introductorio que guíe al lector ya por el uso concreto del framework desarrollado para el presente informe: como crear un simple Web Service que implemente el algoritmo de ordenamiento topológico, creación de un objeto de dominio, lectura y escritura de propiedades, manipulación de colecciones, ejecución de acciones y consulta de los meta datos de tipos, todo mediante la interfaz pública HTTP.

• Capítulo 5 - Diseño e implementación del framework: presentaremos un análisis de la arquitectura y decisiones de diseño que se tomaron para la implementación: requerimientos funcionales y no funcionales, vista de componentes general, análisis de las clases más relevantes tanto del subcomponente DomainModel como del Router, ejemplificación de diagramas de secuencia y la implementación de testing del framework. También analizaremos las características de meta programación de Ruby que se utilizaron.

• Capítulo 6 - Una instanciación del framework: realizaremos un análisis de una instanciación más compleja, ya en una aplicación WEB del tipo SPA (Single Page Application) que

consumirá los recursos de un backend creado con Restful Objects para Ruby. Se presentarán las GUI más relevantes junto con el diseño y su mecanismo de consumo de recursos del frontend.

• Capítulo 7 – Distribución y documentación: demostraremos la metodología de empaquetado, distribución e instalación del framework. Luego, a modo de documentación, presentaremos la sintaxis relevante de la API que se expone desde la perspectiva de un usuario que necesita instanciar una aplicación.

(11)
(12)

Capítulo 2 – Contexto

2.1 Introducción

En el presente capítulo analizaremos brevemente las distintas tecnologías que son previas y claves para la comprensión del estándar Restful Objects y la implementación del framework. Haremos hincapié en los factores más importantes que las destacan, cómo han evolucionado y su influencia en el presente trabajo. Inicialmente hablaremos sobre el patrón de diseño Naked Objects y Domain Driven Design para comprender la importancia de los objetos de dominio. Luego, analizaremos una posible

categorización de los servicios Web, un repaso sobre el protocolo HTTP y las arquitecturas denominadas RESTful. A continuación, analizaremos el estándar Restful Objects y dos

implementaciones tanto de clientes como servidores, específicamente Arow y Apache ISIS. Por último, comentaremos por qué se eligió el lenguaje de programación Ruby para implementar el framework de este trabajo.

2.2 Naked Objects

El patrón de diseño Naked Objects fue descrito formalmente por primera vez en la tesis doctoral de Richard Pawson. El autor creo el patrón de diseño con el objetivo de contar con ciclos de desarrollo más rápidos, mayor facilidad para modificar los sistemas ante cambios en las reglas del negocio y una interfaz de usuario autogenerada de acuerdo a los objetos de dominio. Por lo tanto, en NakedObjects, los objetos de dominio son mostrados al usuario por medio de una capa de presentación totalmente genérica, y toda la funcionalidad referida al dominio o reglas del negocio, debía estar necesariamente contenida en la capa de objetos (Pawson, 2004).

En el capítulo 7 de la mencionada tesis se hizo una comparación entre una implementación con NakedObjects y una arquitectura convencional de cuatro capas. Se usaron métricas para comparar ambas y el resultado fue que la primer implementación requería una cantidad inferior de trabajo para desarrollarse y era menos compleja.

Se crearon dos frameworks que soportaban la arquitectura, ambos basados en el trabajo del autor:

(13)

2. “The Naked Objects Framework” es un proyecto open source creado en Java, y utiliza los mecanismos de reflexión de dicho lenguaje para crear las vistas genéricas sobre los objectos. El framework fue inicialmente creado por Robert Matthews (Pawson & Matthews, 2002). Este mismo framework es el que luego fue renombrado como Apache ISIS y se creo otro port a la tecnología de Microsoft conocido como “Naked Objects for .Net” (http://nakedobjects.net).

2.2.1 Naked Objects y la Web

En la última década cualquier aplicación que requiera cierta interoperabilidad y va a ser consumida desde distintas ubicaciones será candidata a implementarse como un Web Service. Si bien la

arquitectura de Naked Objects seguía siendo pertinente en relación a los objetos de dominio, la única comunicación con el mundo exterior era a través de la interfaz de usuario generada automáticamente por la capa de presentación. Esto las hacía efectivas para ejecutar de forma local en la Intranet de una empresa u organización, pero claramente existía un problema de escala a medida que los servicios provistos por la aplicación necesitaban ser consumidos por más usuarios y en ubicaciones geográficas más dispersas.

Sin embargo, analizando la popularidad de Naked Objects como framework en Internet, nunca tuvo una explosión de usuarios en relación a lo que prometía. Tal vez sea porque en el momento que se creó el framework Internet estaba en sus comienzos por lo que no se implementó ninguna funcionalidad que tuviera en cuenta esta nueva tecnología que estaba emergiendo con fuerza.

2.3 Domain Driven Design

De forma complementaria a lo expuesto en el apartado anterior, es interesante comentar sobre el trabajo de (Evans, 2003), que creó el concepto de DDD (Domain Driven Design), que no es más que una forma de atacar el desarrollo de software de acuerdo a las siguientes premisas:

• Para la mayoría de los proyectos de software, el énfasis principal deberá estar enfocado en el dominio y en las reglas de negocio.

• Diseños de dominio complejos deberán estar basados en modelos.

• Es clave la interacción entre desarrolladores y expertos del dominio para iterativamente refinar un modelo conceptual que solucione los problemas del usuario.

(14)

a una escalada de la complejidad en sus dominios, por lo que, es donde los desarrolladores deberían destinar la mayoría de sus esfuerzos, en vez de en la parte técnica y tecnológica.

Pero ¿cómo enfocarse en el modelo de dominio y abstraer los componentes técnicos del software? Una respuesta posible puede ser a través del uso de un framework para el desarrollo de software. Este framework tiene que encargarse de realizar la parte técnica de la aplicación y liberar al desarrollador lo más posible de los detalles de los protocolos, formatos de comunicación, etc. A la vez tiene que ser poco intrusivo en los objetos de dominio que el desarrollador compone.

2.4 El protocolo HTTP

Para poder comprender el estándar Restful Objects y el framework desarrollado es necesario recalcar algunos conceptos básicos respecto del protocolo HTTP sobre el cual es implementado. En esta sección realizaremos un breve repaso de los elementos que componen el mismo, en especial en relación a RESTful.

2.4.1 Una interfaz uniforme mediante métodos HTTP

El standard HTTP define los siguientes métodos que serán de nuestro interés,

GET: es el método más común, solicita la representación de un recurso del servidor.

PUT: método usado comúnmente para modificar o crear un nuevo recurso.

POST: método usado para agregar un recurso al servidor, generalmente dentro de una colección o un recurso padre.

DELETE: método para eliminar un recurso en el servidor.

Si bien existen otro métodos, estos cuatro nos permiten realizar cualquiera de las operaciones más comunes sobre un recurso (lectura, escritura, creación y eliminación) de forma uniforme para toda la web. Compárese esta simplicidad con la de un Web Service SOAP que los métodos pueden ser definidos por los diseñadores y su semántica deberá ser aprendida por cada usuario del servicio.

2.4.2 Direccionamiento de los recursos

(15)

siempre se tratará del mismo recurso, ignorando temas relacionados a la autorización de usuarios. La forma de direccionar un recurso en el protocolo HTTP es a través de su URL, que es informada luego del método, ejemplo:

GET /application/resources/12202 HTTP/1.1

Si bien este parece un tema trivial, permite que las direcciones se puedan intercambiar fácilmente y mejora la usabilidad del servicio, si en contraposición la misma se encuera oculta dentro de un XML que está embebido en un mensaje SOAP, por ejemplo.

2.4.3 Encabezados HTTP

Los encabezados HTTP se encuentran antes del cuerpo del mensaje en la forma de un conjunto de variables y valores. La función de estos es agregar información, generalmente meta datos, al mensaje para facilitar el entendimiento semántico entre cliente y servidor. Cada encabezado se compone del nombre, seguido por un “:”, espacios en blanco opcional y el valor de la propiedad finalizando con un carácter de cambio de línea.

Existen distintos tipos de encabezados (Gourley et al., 2001),

Generales: pueden aparecer tanto en los mensajes de solicitud como de respuesta.

Solicitud: proveen más información respecto de un mensaje del cliente.

Respuesta: proveen más información respecto a un mensaje del servidor.

Entidad: proveen más información respecto al cuerpo del mensaje.

Extensión: no están especificados en el estándar.

Como se verá, los encabezados cumplen un rol esencial en el estándar Restful Objects.

2.4.4 Cuerpo de la entidad HTTP

(16)

2.4.5 Códigos de respuesta HTTP estandarizados

Otra característica común de una arquitectura tipo RESTful es la utilización de los códigos de respuesta del protocolo HTTP. Generalmente cuando un Web Service tipo RPC recibe una solicitud, debe generar un código de respuesta informando el resultado de la misma, pero ¿por qué reinventar la rueda si HTTP ya cuenta con dicha codificación que es aplicable a la mayoría de los dominios?

El siguiente es un listado de los códigos de respuesta HTTP más comunes en la práctica,

200 - OK: la operación fue realizada con éxito.

301 - Moved Permanently: el recurso fue movido permanentemente a esta URL.

400 - Bad Request: hubo un problema con la solicitud que generó el cliente.

404 - Not Found: no se encontró el recurso solicitado.

500 - Internal Server Error: se produjo un error del lado del servidor.

2.5 Análisis de los distintos tipos de servicios Web

Intentaremos clasificar los distintos servicios Web de acuerdo al estilo arquitectónico que representan. No hay un consenso general entre los desarrolladores de software respecto a estas clasificaciones, pero empezaremos con una serie de definiciones sobre las distintas tecnologías involucradas según

(Richardson & Ruby, 2007). En su trabajo ellos proponen que existen tres principales arquitecturas en los que se pueden clasificar los Web Services.

2.5.1 Arquitecturas tipo RPC (Remote Procedure Call)

Son aquellas en las que el servicio acepta un mensaje (envelope) con información y devuelve un mensaje similar pero la acción o método a efectuar y el alcance o direccionamiento (scope) están dentro del mismo mensaje. Un ejemplo de esto son los servicios Web SOAP, que a la vez son

generalmente embebidos en mensajes HTTP (aunque en teoría podrían transportarse por otros medios como SMTP). XML-RPC también clasifica dentro de este estilo arquitectónico.

2.5.2 Arquitecturas RESTful y Resource-Oriented

(17)

criterios lo fijó Roy Fielding, también colaborador en el desarrollo del protocolo HTTP, en su trabajo de tesis doctoral (Fielding, 2001). En este estilo arquitectónico el método o acción es precisamente el método HTTP y no es necesario reinventar en cada nueva aplicación un distinto vocabulario que agrega complejidad. Además, el direccionamiento que identifica al recurso debe encontrarse en la URL. Estos dos últimos criterios no son completos, pero si determinantes para analizar si un Web Service puede ser considerado RESTful.

2.5.3 Arquitecturas híbridas REST-RCP

Son aquellas que se encuentran entre medio de las dos anteriores y que generalmente violan alguna de las dos premisas respecto al lugar del método o del direccionamiento, pero no llegan a considerarse del todo arquitecturas tipo RPC.

2.6 RESTful versus SOAP y WSDL

Uno de los motivos principales por los cuales es más sencillo trabajar con arquitecturas RESTful es debido a que son más predecibles semánticamente hablando. Una vez que comprendemos los recursos que expone el dominio en cuestión, no es complejo predecir como se combinarán los métodos HTTP para realizar las distintas acciones semánticas. En una arquitectura RCP, tendríamos que aprender las nomenclaturas de los métodos a llamar para las distintas operaciones y dependemos de que el

diseñador haya mantenido una cierta coherencia.

También otro punto a favor de RESTful es que el acoplamiento o coupling entre cliente y servidor es más laxo, lo que provee más oportunidades de evolucionar la implementación en el servidor sin que los clientes existentes dejen de funcionar. Por ejemplo, es común en una implementación RESTful que una representación devuelva un conjunto de links en la forma de pares de atributos de relación y URLs como vínculos a otros recursos relacionados relevantes al actual. Los clientes, indexarán mediante el atributo de relación y luego seguirán el link contenido en el valor de la propiedad, de manera que la forma de diseñar las URLs pueda variar sin romper el funcionamiento del cliente. En una arquitectura RCP, un cambio de nombre de un procedimiento seguramente requiera un cambio en los clientes existentes que lo usan. Lo primero es conocido como HATEOS o “Hypermedia as the Engine of Application State”.

(18)

de caching, por lo que será necesario el reenvío del recurso en todos los casos, aún sabiendo que el mismo posiblemente no cambiará en un periodo de tiempo considerable.

2.7 Ruby: un lenguaje dinámico

El presente trabajo intenta expandir las opciones para frameworks del servidor de la especificación Restful Objects, creando Restful Objects para Ruby. A continuación presentaremos un conjunto de razones relevantes para utilizar dicho lenguaje en la implementación.

• Ruby es un lenguaje dinámico, reflexivo, orientado a objetos y de uso general ampliamente utilizado en el ambiente de aplicaciones en Internet (Thomas et al., 2009). Por ejemplo, fue la base para crear populares aplicaciones como Twitter, Grupon, Github, Shopify, Airbnb y otras [https://prograils.com/posts/top-10-famous-sites-built-with-ruby-on-rails].

• Yukihiro Matsumoto, el creador del lenguaje, dice él: “Espero que Ruby ayude a cada

programador en el mundo a ser productivo, y a disfrutar de la programación y ser feliz con su trabajo. Ese es el objetivo principal de Ruby” [http://www.artima.com/intv/rubyP.html].

(19)

Capítulo 3 – La especificación Restful Objects

3.1 Introducción

El presente capítulo introducirá la especificación Restful Objects al lector, de forma que adquiera los conocimientos mínimos necesarios para entender el funcionamiento del framework. Un análisis completo del estándar está fuera del alcance del presente trabajo, ya que la especificación cuenta con más de 200 páginas, por lo que solamente se destacarán los elementos más relevantes de la misma. Restful Objects fue creado por Dan Haywood como aporte al proyecto Apache Isis para proveerle al mismo una interfaz de conexión con el mundo exterior por medio de Web Services. La versión 1.0, sobre la cual el framework se basa, fue publicada en Junio del 2012.

3.2 Objetivos

Restful Objects es una especificación pública para un conjunto de recursos RESTful por los cuales una aplicación cliente puede interactuar con los objetos de dominio en el servidor a través del protocolo HTTP. El servidor generará representaciones en formato JSON de dichos recursos en conjunto con distintos encabezados HTTP, entre ellos “Content-Type” que lo identifica. De esta forma. las representaciones pueden ser consumidas por clientes implementados en distintos lenguajes de programación tales como Java, Ruby, Python, .NET o similares a través de Internet. Los clientes pueden ser genéricos, iniciando el consumo del Web Service por los puntos de entrada conocidos y generando una interfaz gráfica genérica de acuerdo a la información de meta datos generada por el framework o específicos, es decir, cuentan con información a priori de las clases y servicios que expone el dominio.

Restful Objects es una especificación de alto nivel de abstracción y tiene pocas presunciones acerca del dominio de objetos subyacente. En términos genéricos un objeto de dominio cuenta con:

Propiedades: que pueden ser un valor simple como cadena, entero, fecha, hora, blob binario o una referencia a otro objeto.

Colecciones: un conjunto de referencias a otros objetos.

(20)

3.3 Interfaz uniforme

RestfulObjetcs define una interfaz uniforme al dominio de objetos, que consiste en,

• Un conjunto de URLs estandarizadas para acceder a los recursos subyacentes.

• La semántica de los métodos HTTP utilizados para interactuar con los recursos.

• Los encabezados HTTP soportados tanto por las solicitudes como las respuestas.

• Los códigos de respuesta HTTP estándar utilizados.

• Descriptores del tipo de contenidos o “media types” estandarizados.

• Propiedades JSON estandarizadas en las representaciones de los recursos.

• Una representación estandarizada de los links para navegar hacia otros recursos.

Para más detalles consultar (Dan Haywood, 2012).

3.4 Recursos y representaciones

El siguiente diagrama, extraído del estándar, muestra los recursos y representaciones definidas por la especificación.

• Todos los recuadros oscuros identifican un recurso: los amarillos son recursos de soporte para estándar, los rosas están relacionados con los objetos del dominio y por último los celestes están relacionados con la información de meta datos de las clases.

• La mayoría de los recursos muestran una línea hacia un recuadro transparente que es la representación obtenida que en la mayoría de los casos es compartida por varios recursos.

• Los círculos en los recuadros representan uno de los cuatro métodos HTTP estándar: GET, PUT, POST y DELETE; la ausencia del círculo indica que el recurso no soporta el método.

(21)

relevantes se encuentran presentes para no saturar el diagrama.

Figura 1: Diagrama de recursos, representaciones y métodos HTTP para RestfulObjects

3.5 Identificación del “Content-type” estandarizado

En el estándar HTTP existen dos encabezados ampliamente utilizados y relacionados con el tipo del contenido que transporta el protocolo,

(22)

“Accept” se encuentra solamente en mensajes de solicitud y le indica al servidor que

representaciones el cliente está capacitado para manejar, de esta forma el servidor no enviará una representación que el solicitante tenga que descartar y responderá con un mensaje de error estandarizado código 406 (not acceptable), si se produjese dicha situación.

Los navegadores utilizan dicha información todo el tiempo para determinar como mostrar un

contenido: por ejemplo “text/html” para documentos HTML, “image/png” o “image/svn” para tipos de imágenes y similar. En vez de definir un conjunto propio de “media types” la especificación utiliza “application/json” y luego emplea los denominados parámetros para refinar la misma. De esta forma se dice que existen distintas capas de especificación.

Por ejemplo supongamos que realizamos un GET para obtener la representación de un objeto de la clase “Factura”, el framework en el mensaje de respuesta devolvería en el encabezado “Content-Type”:

application/json;profile=“urn:org.restfulobjects:repr-type/object”;x-ro-domain-type=“Factura”

La primer capa indica que se trata de una respuesta en formato JSON, de esta forma ya puede ser consumido por un cliente genérico que no contenga conocimiento a priori de la especificación. Para un listado completo de los “media-type” estandarizados por IANA consultar la referencia

[http://www.iana.org/assignments/media-types/media-types.xhtml]. El segundo parámetro se encuentra a nivel del framework e indica que se trata de un objeto del dominio, un cliente genérico de Restful Objects podrá utilizar la misma para saber esto. La tercera capa, mediante el valor del parámetro “x-ro-domain-type”, indica el nombre de la clase y es una información propia de cada dominio que será ignorada determinados clientes pero un consumidor específico del Web Service que ya contenga información de las clases expuestas puede utilizarla para realizar una visualización más adaptada a la misma.

A continuación presentamos una tabla con los “profile” definidos por el estándar:

urn:org.restfulobjects:repr-type/homepage página de inicio

urn:org.restfulobjects:repr-type/user información del usuario identificado

(23)

urn:org.restfulobjects:repr-type/action-result resultado de ejecutar una acción de un objeto del dominio urn:org.restfulobjects:repr-type/type-list lista de tipos de objetos de dominio

urn:org.restfulobjects:repr-type/domain-type tipo de objeto de dominio

urn:org.restfulobjects:repr-type/property-description descripción de una propiedad de un tipo de objeto de dominio urn:org.restfulobjects:repr-type/collection-description descripción de una colección de un tipo de objeto de dominio urn:org.restfulobjects:repr-type/action-description descripción de una acción de un tipo de objeto de dominio urn:org.restfulobjects:repr-type/action-param-description descripción de un parámetro de una acción de un tipo de objeto

de dominio

urn:org.restfulobjects:repr-type/type-action-result descripción del resultado de una acción de un tipo de objeto de dominio

urn:org.restfulobjects:repr-type/error error

3.6 Valores de datos soportados y formatos

El estándar define la propiedad “format” para interpretar los valores de las propiedades:

string: cadena de caracteres, en este caso la propiedad “format” es opcional.

int: valor entero.

big-integer(n): un valor entero grande con escala “n”.

decimal: valor de punto flotante.

big-decimal(s, p): interpretado como un valor decimal grande con escala “n” y precisión “p”.

date-time: una fecha en el formato ISO 8601 “YYYY-MMDDThh:mm:ssZ” en tiempo UTC.

date: una fecha en el formato “YYYY-MM-DD”

time: una hora en el formato “hh:mm:ss”

utc-millisec: la diferencia, medida en mili-segundos, entre la fecha especificada y las 00hs del 01/01/1970 (Unix time format).

blob: un objeto binario representado como una cadena codificada en base-64.

(24)

3.7 Formato de referencias a otros objetos

Cualquier representación en JSON puede tener una o más relaciones con otras representaciones y las mismas se describen mediante un link estándar con el siguiente formato:

{

“rel”: “”,

“href”: “http://xxx/yyy”,

“type”: “application/json;profile=\“urn:org.restfulobjects:repr-type/xxx\”, “method”: “GET”

}

Si bien un link puede tener más propiedades, las anteriores cuatro son las mínimas indispensables que todos debe proveer de acuerdo al estándar,

“rel”: indica la naturaleza de la relación del recurso relacionado con el generado actualmente.

“href”: la URL absoluta del recurso relacionado.

“type”: el tipo de contenido del recurso relacionado, como se analizó anteriormente.

“method”: el método HTTP usado para obtener la representación.

3.8 Objetos del dominio

Un objeto del dominio es de alguna forma el actor central de la especificación. De acuerdo a la naturaleza del objeto de dominio representado, pueden existir links a uno o más recursos relacionados,

• a propiedades y colecciones del objeto

• a acciones del objeto

• para actualizar más de una propiedad del objeto en una sola transacción

• para persistir un objeto

• para eliminar un objeto

En la siguiente tabla se presenta un resumen de como acceder a cada recurso relacionado y las

(25)

inequívoco de una instancia de objeto y {NAME} un nombre de propiedad, colección o acción según el caso: Objects/ {TYPE} Objects/{TYPE}/ {ID} Objects/{TYPE}/ {ID}/Properties/ {NAME} Objects/{TYPE}/ {ID}/Collections/ {NAME} Objects/ {TYPE}/ {ID}/Actions/ {NAME} Objects/{TYPE}/ {ID}/Actions/ {NAME}/Invoke GET error 405 obtiene detalle del

objeto y valores de propiedades

obtiene detalles y valor de la propiedad

obtiene detalles y contenido de la

colección

obtiene detalles

de la acción efectos colaterales)invoca acción (sin

PUT error 405 actualiza múltiples

propiedades actualiza valorpropiedad agrega objeto acolección (semántica de

conjuntos)

error 405 invoca acción (idempotente)

DELETE error 405 elimina objeto elimina valor

propiedad elimina un objetode colección error 405 error 405

POST persistir

instancia error 405 error 405 agregar objeto acolección (semántica de

lista)

error 405 invoca acción (no idempotente)

3.8.1 Ejemplos de solicitudes HTTP interactuando con objetos del dominio

A continuación presentamos una serie de solicitudes HTTP con URLs de ejemplo en un supuesto dominio de objetos referido a facturación comercial. Para lo mismo vamos a suponer nuestro Web Service se encuentre ejecutándose en el servidor http://app.gestion.com.ar:

GET http://app.gestion.com.ar/Objects/Factura/426631

Consulta el recurso Factura con identificación 426631. Suponiendo el mismo exista el servidor devolverá una representación JSON del mismo que contendrá los valores de las propiedades, meta datos acerca de la misma y un listado de las acciones disponibles.

POST http://app.gestion.com.ar/Objects/Factura

Crea un recurso en el servidor del tipo Factura. El cuerpo del mensaje es un JSON que contendrá los valores iniciales de las propiedades del objeto. El servidor responderá con una representación del mismo, entre las cuales se encuentra el InstanceId que identificará al objeto.

GET http://app.gestion.com.ar/Objects/Factura/426631/Properties/importe_total

(26)

respuesta será en formato JSON y además del valor tendrá meta datos que lo describan como la propiedad format.

PUT http://app.gestion.com.ar/Objects/Factura/426631/Properties/fecha_contable

Modifica el valor de la propiedad fecha_contable del objeto tipo Factura con identificador 426631. El cuerpo del mensaje será un JSON que contenga el nuevo valor de la propiedad. La respuesta, en el mismo formato, será una representación del valor devuelto por la misma luego de realizar la

modificación.

GET http://app.gestion.com.ar/Objects/Factura/426631/Collections/renglones

Obtiene una representación de una lista de objetos, denominada renglones, correspondiente al recurso Factura con identificación 426631. Esta lista será representada en JSON como un arreglo de links a otros objetos del dominio, por ejemplo de clase RenglonFactura.

POST http://app.gestion.com.ar/Objects/Factura/426631/Collections/renglones

Agrega un objeto a la colección renglones del objeto Factura con identificación 426631. El objeto ya tiene que existir dentro del servidor y la solicitud en formato JSON contendrá un link al recurso.

POST http://app.gestion.com.ar/Objects/Factura/426631/Actions/procesar/Invoke

Ejecuta la acción procesar del objeto Factura con identificación 426631. El cuerpo de la solicitud en formato JSON contendrá los valores de los parámetros necesarios para ejecutar el método. La

respuesta será en JSON y contendrá el valor devuelto por la misma, si existiese.

3.9 Servicios del dominio

Otro actor relevante dentro de Restful Objects es el de un servicio del dominio. Los mismos son entidades de las que solo existe una instancia, fácilmente identificables y accesibles, que suelen ser los puntos entrada para interactuar con los objetos de la aplicación. En términos de diseño, se puede decir que implementan el patrón Singleton (Gamma et al., 1995). Otra característica que los diferencia de un objeto de dominio es que los mismos sólo pueden contar con acciones, pero no propiedades o

(27)

3.10 Apache ISIS

Apache ISIS es un proyecto descendiente de NakedObjects, renombrado al ser patrocinado por la Apache Software Foundation, para desarrollar de forma rápida aplicaciones en Java basadas en un modelo de dominio. Los usuarios escriben la lógica del negocio en entidades, servicios y repositorios de dominio y el framework automáticamente genera de forma dinámica una representación del mismo en la forma de una aplicación web y/o una API RESTful para consumir mediante un cliente HTTP. Está pensado tanto para hacer rápidos prototipos para descarte como aplicaciones destinadas producción [http://isis.apache.org/index.html].

3.11 Restful Objects for .Net

Restful Objects for .Net es un port de NakedObjects para la plataforma .Net de Microsoft, pensado para desarrollar con su IDE Visual Studio

[https://github.com/NakedObjectsGroup/NakedObjectsFramework]. El proyecto está patrocinado por el Naked Objects Group [http://nakedobjects.net].

Tanto “Apache ISIS” como “Restful Objects for .NET” son frameworks para simplificar el desarrollo

(28)

fuertemente desarrollados en la actualidad, según sus sitios web.

3.13 Clientes genéricos versus específicos

Se podrían considerar dos tipos generales de clientes de un servicio que implementa Restful Objects, de acuerdo al conocimiento que dispongan de los objetos y servicios expuestos por el servidor.

3.13.1 Clientes genéricos

Son aquellos que no tienen un conocimiento previo de los tipos de objetos y servicios que ofrece el servidor, por lo tanto la forma de utilizar dichos servicios es mediante la descripción de tipos, meta datos y URLs estandarizadas que ofrece la especificación. Este tipo de clientes son utilizados por los usuarios, que van tomando decisiones sobre cómo continuar de acuerdo a la información que muestra el cliente en la pantalla. Una ventaja de los mismos, es que pueden reutilizarse para distintos servicios, ya que tienen poco acoplamiento con el servidor y este puede evolucionar de forma independiente. Como contrapartida, no están especializados en ninguno en particular, por ser genéricos.

3.13.2 Clientes específicos

Bajo esta categorización se encuentran los clientes que cuentan con un conocimiento a priori de los tipos de objectos y servicios que ofrece el servidor, por lo tanto no es necesario que utilicen la

(29)

Capítulo 4 – Introducción a Restful Objects para Ruby

4.1 Introducción

Restful Objects para Ruby le permite a los desarrolladores concentrarse en el diseño y codificación de los objetos del domino y abstraerse de la implementación de la interfaz HTTP para interactuar con los mismos. En el presente capítulo mostraremos cómo se transforma una pequeña aplicación Ruby, sin dependencias externas, que implementa el algoritmo de ordenamiento topológico de grafos, a otra que realiza la misma función pero proveyendo una interfaz HTTP compatible con el estándar. Se ha elegido dicho algoritmo ya que es generalmente conocido por los desarrolladores y a la vez lo suficientemente sencillo para permitirle al lector concentrarse en los aspectos concernientes al framework. Luego, ejemplificaremos cómo se convierte una clase estándar de Ruby en una que implementa el protocolo Restful Objects. Posteriormente, se analizará cómo se interactúa con los los meta datos de un objeto de dominio, cómo se crean instancias del mismo y cómo se establecen o leen los valores de las

propiedades. Por último, presentaremos una implementación completa del algoritmo compatible con Restful Objects y un script Ruby como uno basado en Bash, que lo consumen a modo de ejemplo.

Para facilidad del lector, todos los extractos de código en este capítulo son ejecutables y se pueden consultar online en la siguiente URL:

http://github.com/vizcay/RestfulObjectsExamples/tree/master/topological_sort

4.2 Ordenamiento topológico

El siguiente fragmento de código contiene una implementación del algoritmo de ordenamiento topológico implementada en Ruby sin más dependencias que las construcciones del lenguaje básicas:

class Nodo

attr_accessor :nombre, :dependencias, :visitado def initialize(nombre, dependencias = []) @nombre = nombre

@dependencias = dependencias @visitado = false

end def to_s @nombre end end

class Grafo

(30)

end

def ordenamiento_topologico @lista_ordenada = []

nodos_sin_visitar = nodos.dup until nodos_sin_visitar.empty? do visitar(nodos_sin_visitar.pop) end @lista_ordenada end private def visitar(nodo) unless nodo.visitado

nodo.dependencias.each do |dependencia| visitar(dependencia)

end

nodo.visitado = true @lista_ordenada << nodo end

end end

El código está compuestos de las clases Nodo y Grafo. Mediante las mismas es posible crear un grafo acíclico con nodos que tienen dependencias a otros. La clase Grafo cuenta con el método

#ordenamiento_topologico, el cual opera sobre los nodos del grafo, devolviendo un arreglo de los mismos en uno de los posibles ordenamientos de forma que para todos los vértices o dependencias de A hacia B, B se encuentra primero que A en la lista ordenada resultante. El mismo se implementa mediante algoritmo Deep First Search, que navega las dependencias marcando los nodos como visitados (Cormen et al., 2009).

Para el ejemplo en cuestión, cada nodo representará un lenguaje de programación y cuando un nodo depende de otro significará que el mismo tiene precedencia en la historia, por lo que elementos del mismo fueron tomados en cuenta para el diseño del posterior.

Presentamos un ejemplo de su utilización. Primero se instancia la clase Grafo y luego se crean uno a uno los nodos que representan lenguajes. Inicialmente se instancia el nodo para “Ruby” y como dependencias del mismo los lenguajes “Smalltalk”, “Lisp” y “Perl”. Es decir, para el supuesto de nuestro ejemplo, en la creación de Ruby se utilizaron elementos de dicho lenguaje y en todo

ordenamiento topológico resultante estos últimos deben aparecer antes del mismo. Luego, se agregan todos nodos al grafo y por último se llama al método #ordenamiento_topologico que produce el arreglo o lista que es impreso por pantalla mediante el método #puts.

require “topological_sort_1” grafo = Grafo.new

assembler = Nodo.new('Assembler')

fortran = Nodo.new('Fortran', [assembler])

(31)

lisp = Nodo.new('Lisp', [assembler]) smalltalk = Nodo.new('Smalltalk', [fortran]) perl = Nodo.new('Perl', [c])

ruby = Nodo.new('Ruby', [smalltalk, lisp, perl]) grafo.nodos << assembler << c << fortran << lisp << perl << ruby << smalltalk puts grafo.ordenamiento_topologico

Al ejecutar el código mediante el interprete Ruby obtendremos el siguiente resultado,

$ ruby topological_sort_example.rb

Assembler Fortran Smalltalk Lisp C Perl Ruby

Debemos resaltar nuevamente que no hemos utilizado más dependencias que los constructores básicos del lenguaje y clases de la librería estándar.

4.3 La implementación en Restful Objects

require 'restful_objects' # 1

class Nodo

include RestfulObjects::Object # 2

property :nombre, :string # 3

collection :dependencias, Nodo # 4

attr_accessor :visitado def initialize

super

@visitado = false end

def nombre=(value)

@nombre = @title = value end

end

class Grafo

include RestfulObjects::Object collection :nodos, Nodo action :agregar_nodo, return_type: { object: Nodo }, parameters: { nombre: :string } # 5

action :ordenamiento_topologico, return_type: { list: Nodo } def agregar_nodo(nombre)

(32)

return nodo end

def ordenamiento_topologico @lista_ordenada = []

nodos_sin_visitar = nodos.dup until nodos_sin_visitar.empty? do visitar(nodos_sin_visitar.pop) end

@lista_ordenada end

private

def visitar(nodo) unless nodo.visitado

nodo.dependencias.each do |dependencia| visitar(dependencia)

end

nodo.visitado = true @lista_ordenada << nodo end

end end

En el fragmento de código presentado se realizaron las modificaciones necesarias para transformar las clases introducidas anteriormente, en unas que implementan el estándar Restful Objects con la ayuda del framework que se desarrolló. Las mismas se encuentra resaltadas y se analizarán a continuación con los códigos de referencia pertinentes:

1. #require es el método de Ruby para cargar en memoria una librería o archivo de código externo, esta línea permite que podamos utilizar el framework.

2. #include RestfulObjects::Object declara que nuestro objeto es público para exponerse en la interfaz del HTTP del Web Service además de incorporar a la clase un conjunto de métodos auxiliares que nos permitirán declarar las propiedades, colecciones y acciones del mismo.

3. #property <name>, <datatype> declara una propiedad del objeto, que será expuesta en la interfaz externa HTTP junto con el tipo de datos que si bien no es relevante para Ruby por ser de tipeado dinámico, es de importancia para el estándar Restful Objects.

4. #collection <name>, <datatype> declara una colección de subobjetos pública en el Web Service, en este caso del tipo Nodo.

(33)

Es de notar que #visitado es un método que no es necesario exponer, ya que solamente es utilizado por la implementación interna del ordenamiento topológico y por lo tanto no ha sido modificado y si bien seguirá siendo público pero no estará disponible en la interfaz del Web Service.

4.4 Iniciar el Web Service

Claramente no hay grandes modificaciones respecto al código original, ya que el framework realiza una gran cantidad de trabajo de forma transparente para el desarrollador. Iniciemos ahora el servidor mediante el comando:

$ restful_server restful_topological_sort.rb

[2015-04-04 15:33:01] INFO WEBrick 1.3.1

[2015-04-04 15:33:01] INFO ruby 2.0.0 (2014-05-08) [x86_64-linux]

== Sinatra/1.4.4 has taken the stage on 4567 for development with backup from WEBrick [2015-04-04 15:33:01] INFO WEBrick::HTTPServer#start: pid=26289 port=4567

...

Ese comando activa un servidor HTTP que se encuentra en la libería estándar de Ruby conocido como WEBrick, ideal para ambientes de desarrollo y con el objetivo de probar nuestro servicio. En la

consola, se observa como WEBrick informa que se encuentra escuchando en el puerto 4567 en localhost, el servidor imprimirá información de depuración por cada solicitud HTTP que reciba.

4.5 Crear un objeto de dominio

Para crear una instancia de una clase determinada, el estándar indica que tenemos que realizar un HTTP POST a una determinada URL, con una representación de los valores iniciales de las

propiedades que deseamos establecer. El formato del JSON debe contener una propiedad “members” y dentro un valor para cada propiedad que deseemos inicializar del objeto al construirlo, siendo posible no indicar ninguna. En el siguiente JSON vemos como el valor de la propiedad “nombre” será

inicializado a “Assembler”:

{

"members": { "nombre": {

"value": "Assembler" }

} }

(34)

require 'json'

Net::HTTP.start('localhost', 4567) do |http|

response = http.post('/objects/Nodo', '{ "members": { "nombre": { "value": "Assembler" } } }') puts JSON.pretty_generate(JSON.parse(response.body))

end

Las primeas dos líneas cargan las librerías estándar para interactuar con HTTP y JSON. Luego se crea un objeto Net::HTTP y se indica que se conecte a http://localhost:4567 qué es donde nuestro Web Service se encuentra escuchando. A continuación se indica a la librería que debe realizar un método HTTP POST a la ruta “/objects/Nodo”, con el contenido del string JSON ya presentado. Por último, la respuesta se interpreta como JSON y se envía al método JSON.pretty_generate que devuelve un string con formato más sencillo de visualizar para los humanos.

Ejecutamos el script y obtenemos con el siguiente resultado:

$ ruby example_create_nodo.rb

{ "instanceId": "70284458070480", "title": "Assembler", "members": { "nombre": { "memberType": "property", "value": "Assembler", "links": [ { "rel": "urn:org.restfulobjects:rels/details;property=\"nombre\"", "href": "http://localhost:4567/objects/Nodo/70284458070480/properties/nombre", "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\"", "method": "GET" } ],

"extensions": { .. } }, "dependencias": { "memberType": "collection", "size": 0, "links": [ { "rel": "urn:org.restfulobjects:rels/details;collection=\"dependencias\"", "href": "http://localhost:4567/objects/Nodo/70284458070480/collections/dependencias", "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-collection\"", "method": "GET" } ],

(35)

"method": "GET" }

],

"extensions": { .. } }

}

Al crear un objeto, el framework devuelve una representación del mismo en JSON como respuesta, analizaremos las secciones más relevante de interés.

• La propiedad “instanceId” es un identificador inequívoco del objeto y que el framework utiliza para generar las URLS por las cuales se interactúa por el mismo. Esto se puede visualizar en el arreglo de “links”, la entrada“self” tiene como valor “href”:

“http://localhost:4567/objects/Nodo/70284458070480" que está formado precisamente con el “InstanceId” en cuestión. Por defecto Restful Objects utiliza el entero devuelto por Object#id que de acuerdo a la documentación de Ruby no es más que la dirección en memoria del objeto, pero es posible realizar un “override” de este método para utilizar una clave primaria de una base de datos u otro dato definido por el programador.

• Observar la propiedad “nombre”, dentro de “members” y el valor que le asignamos por defecto en el JSON de entrada es el que contiene actualmente así como “memberType” establecido en “property”.

• También dentro de “members” podemos encontrar a “dependencias” qué es un “memberType” del tipo “collection”, es decir nuestra colección de dependencias a otros Nodos. En el caso de las colecciones el conjunto de elementos que las componen se deben consultar por la URL específica aunque sabemos que tiene 0 elementos momentáneamente:

"http://localhost:4567/objects/Nodo/70284458070480/collections/dependencias".

Es importante destacar que lo que estamos analizando es una representación del objeto Nodo, pero el verdadero recurso es una instancia en memoria que se encuentra en nuestra aplicación. En sólo un método HTTP, el framework realizó una serie de acciones de forma transparente:

1. Procesó e interpretó la URL junto con el método HTTP POST.

2. Analizó estructuralmente el JSON de entrada.

(36)

4. Estableció el valor por defecto de las propiedades especificadas.

5. Retornó como resultado la representación JSON actual del nuevo objeto.

El siguiente diagrama es una representación visual de lo acontecido:

4.6 Leer y escribir propiedades

En el ejemplo anterior hemos creado una instancia de un objeto en memoria, pero no hemos

interactuado con la misma. Realizaremos en esta sección un ejemplo de como se puede leer y escribir una propiedad. Las propiedades son consideradas un recurso y tienen un URL independiente de la del objeto de dominio, aunque es posible consultar su valor directamente de la representación del mismo.

require 'net/http' require 'json'

Net::HTTP.start('localhost', 4567) do |http|

response = http.post('/objects/Nodo', '{ "members": {} }') # 5

if response.code == '200'

nodo_url = JSON.parse(response.body)['links'].find { |link| link['rel'] == 'self' }['href'] # 7

language = ['Assembler', 'C', 'Fortran', 'Lisp', 'Smalltalk', 'Perl', 'Ruby'].sample # 8

(37)

http.put("#{nodo_url}/properties/nombre", '{ "value": "' + language + '" }') # 9

response = http.get("#{nodo_url}/properties/nombre") # 11

if response.code == '200'

puts "Valor propiedad 'nombre': #{JSON.parse(response.body)['nombre']['value']}" # 13

else

puts "Error: #{response.code}" end

end end

El script presentado empieza creando un objeto Nodo sin inicializar ninguna propiedad en la línea #5, verifica si la respuesta HTTP corresponde al código 200 (“HTTP OK”) y luego en #7 parsea el JSON devuelto accediendo al link con propiedad “rel” == “self” en la variable nodo_url para referirse al Nodo; para lo mismo se utiliza el método Array#find que devuelve el primer elemento del arreglo para el cual el bloque de código pasado como parámetro devuelve true. En la línea #8 utiliza el método #sample sobre un arreglo lo cual devuelve uno de los elementos de forma aleatoria quedando asignado en la variable language. De forma posterior en la línea #9 se realiza una solicitud HTTP PUT en una URL que identifica la propiedad “nombre” del objeto Nodo creado utilizando. El cuerpo del mensaje es un JSON donde la propiedad “value” se asigna el valor de la variable “language”, de esta forma vemos como se escribe una propiedad. Luego en #11 se realiza una solicitud HTTP GET a la misma URL que ahora tiene la semántica de leer el valor de la propiedad. Por último en la línea #13 se muestra por pantalla el resultado si el código de respuesta fue '200', primero siendo parseado el JSON de la solicitud y luego accediendo a las propiedades “nombre” y “value” dentro de la misma.

La siguiente es una ejecución del anterior código con una de las posibles salidas:

$ ruby example_read_write_property.rb

Valor propiedad 'nombre': Smalltalk

4.7 Agregar un elemento a una colección

A continuación veremos como se agregan elementos una colección, para lo mismo utilizaremos la colección #nodos de la clase Grafo.

require 'net/http' require 'json'

Net::HTTP.start('localhost', 4567) do |http|

(38)

# 6

collection_url = "/objects/Grafo/#{grafo_id}/collections/nodos" # 7

response = http.post('/objects/Nodo', '{ "members": { "nombre": { "value": "Smalltalk" } } }') # 9

nodo_smalltalk = JSON.parse(response.body)['instanceId'] http.post(collection_url, # 12

'{ "value": { "href": "http://localhost:4567/objects/Nodo/' + nodo_smalltalk + '" } }') response = http.get(collection_url) # 15

puts JSON.pretty_generate(JSON.parse(response.body)['value']) # 16

end

En la línea #5 se crea un objeto Grafo y luego de parsear la respuesta se almacena el “instanceId” en la variable “grafo_id” en #6 y se crea la URL para referenciar la colección “nodos” para usar

posteriormente en #7. Luego en la línea nueva se envía una nueva solicitud HTTP POST y se crea el nodo “Smalltalk” guardando el “instanceId” correspondiente en la variable “nodo_smalltalk”. Posteriormente se envía una solicitud HTTP POST a la URL de la colección con un JSON como cuerpo que contiene en la propiedad “value” otra “href” con valor de la URL del objeto nodo Smalltalk creado anteriormente, de esta forma se agrega un elemento a una colección. En la línea #15 se realiza una solicitud GET nuevamente a la URL de la colección, que tiene la semántica de consultar los contenidos de la colección. Por último en #16 se parsea la respuesta como JSON y se imprime por pantalla el valor de la propiedad “value” que es el contenido de la colección.

A continuación presentamos el resultado de una ejecución de dicho código:

$ ruby example_append_collection.rb

[ {

"rel": "urn:org.restfulobjects:rels/value;collection=\"nodos\"", "href": "http://localhost:4567/objects/Nodo/70227149489480",

"type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object\"", "method": "GET",

"title": "Smalltalk" }

]

4.8 Ejecutar una acción

(39)

#agregar_nodo(nombre) que realiza todos estos pasos y devuelve una referencia al nodo recientemente creado.

El siguiente fragmento de código en Ruby es un ejemplo de como lograr dicho resultado:

require 'net/http' require 'json'

Net::HTTP.start('localhost', 4567) do |http|

response = http.post('/objects/Grafo', '{ "members": { } }') # 5

grafo_id = JSON.parse(response.body)['instanceId']

action_invoke_url = "/objects/Grafo/#{grafo_id}/actions/agregar_nodo/invoke" # 8

response = http.post(grafo_url, '{ "nombre": { "value": "Fortran" } }') # 9

action_result = JSON.parse(response.body)

puts 'Title: ' + action_result['result']['title'] # 12

puts 'InstanceId: ' + action_result['result']['instanceId'] end

Igual que en los ejemplos anteriores el script empieza abriendo una conexión HTTP local con el puerto 4567 donde nuestro Web Service se encuentra escuchando. Luego en la referencia #5, crea un objeto de la clase Grafo mediante un mensaje POST como ya hemos explicado, parseando la respuesta recibida en JSON y guardando el “instanceId” del objeto resultante para luego poder generar el URL y ejecutar la acción correspondiente en #8.

En la linea #9 vemos como el script ejecuta otro POST, pero esta vez a la URL generada para

interactuar con la acción y como cuerpo del mensaje, un JSON con los parámetros a pasar a la misma. En el caso en particular, el único parámetro del método #agregar_nodo es nombre y el argumento tiene el valor “Fortran”. En este momento el servidor ejecuta del método correspondiente y genera la respuesta en JSON para representar el nodo recientemente creado:

def agregar_nodo(nombre) nodo = Nodo.new

nodos.nombre = nombre nodos << nodo

return nodo end

(40)

$ ruby example_add_nodo.rb

Title: Fortran

InstanceId: 69849022898540

4.9 Interactuando con el Web Service desde la terminal

Realizaremos a continuación un conjunto de pruebas de interacción con nuestro Web Service y

utilizaremos para lo mismo dos herramientas de la línea de comandos que nos permitirán especificar y procesar solicitudes HTTP que son utilizadas en ambientes Linux, aunque existen ports para otras plataformas.

4.9.1 Interacciones HTTP con “curl”

La misma es una herramienta de línea de comandos que por defecto realiza una solicitud GET a la URL pasada como primer parámetro y envía el cuerpo de la respuesta a la salida estándar, lo que hace posible encadenarla con otra aplicación mediante las “pipes” o cañerías de Unix. El parámetro “--silent” suprime toda otra clase de mensajes en la salida (como el progreso de la descarga). En el caso de querer realizar una solicitud post, es necesario pasar el parámetro “--data” y uno de los mecanismos de enviar el cuerpo de la solicitud es mediante el parámetro “@-” que indica que se debe leer de la entrada estándar. Se puede consultar más información sobre curl en la página web de la herramienta en [http://curl.haxx.se/].

curl <options> [URL] curl http://localhost:4567

curl --silent http://localhost:4567/

4.9.2 Formateo y manipulación de JSON con “jq”

La misma es también una herramienta de línea de comandos, que lee JSON de la entrada estándar y realiza distintas operaciones de acuerdo a los parámetros indicados. En el script utilizaremos como filtro el operador “.” que accede de forma descendente dentro de las propiedades del JSON y devuelve el valor de la propiedad. El operador “[]” extrae los valores de un arreglo. El parámetro “-r” sirve para eliminar las comillas de los valores de propiedades que son strings, de forma que sea más sencilla la extracción del valor. Para más información sobre jq consultar la página web de la misma en

[https://stedolan.github.io/jq/].

4.10 Consultando los meta datos de las clases

Referencias

Documento similar