In questo articolo ci occupiamo principalmente delle fasi necessarie alla generazione e fruizione di Web Services in Rails. Faremo ricorso a AWS, Action Web Services e alla libreria SOAP::WSDLDriverFactory. Cerchiamo di arricchire con preziose informazioni le varie fasi del processo.
Introduzione
Non ci siamo ancora occupati (ma ce ne occuperemo nei prossimi articoli) del classico front-end Web basato su pagine HTML, eppure ci immergiamo direttamente nel mondo dei Web Services.
In un contesto in cui le tecnologie di integrazione costituiscono una necessità continua, abbiamo dato preferenza a tale aspetto e i Web Services sono, senza dubbio, la tecnologia di riferimento per l‘integrazione di sistemi eterogenei.
In luogo degli strumenti a riga di comando finora adoperati nei precedenti articoli, utilizzeremo RadRails come IDE di sviluppo.
Andremo a creare due applicazioni, una per descrivere i passi necessari alla realizzazione di un Web Service da mettere a disposizione per essere fruito, l‘altra per consumare un servizio disponibile. Non trascureremo la fase di test del servizio.
Nell‘effettuare queste operazioni cercheremo di toccare vari aspetti, tutti pragmaticamente interessanti per lo sviluppatore.
La prima applicazione
Iniziamo subito con la fase operativa. Apriamo RadRails (vedi Ruby II per i riferimenti) e creiamo il progetto Rails MokabyteWS. Con un classico File -> New -> Rails Project otteniamo il wizard per la creazione di un progetto Rails.
Figura 1 – Creazione di un progetto con RadRails
Inseriamo il nome del progetto (MokabyteWS nel nostro caso) e, lasciando le impostazioni di default “Generate Rails Application Skeleton” e “Create a Web Brick Server”, clicchiamo su ‘Finish‘. La struttura del progetto, a meno di imprevedibili (!!!) errori, risulterà creata.
Come selezionato con il check avremo a disposizione anche un server sul quale far girare le nostre applicazioni.
Figura 2 – Server WebBrick disponibile per il progetto
A questo punto utilizziamo la base dati utilizzata nell‘articolo precedente (RubyIII) che contiene giusto la nostra tabella “countries”. È nostra intenzione consentire la fruizione del contenuto della tabella via Web Services.
Questo il contenuto dello script.
ÃÂ ÃÂ CREATE TABLE `countries` (
ÃÂ ÃÂ ÃÂ `id` int(11) NOT NULL auto_increment,
ÃÂ ÃÂ ÃÂ `name` varchar(128) collate utf8_bin NOT NULL,
ÃÂ ÃÂ ÃÂ `continent` enum(‘Asia‘,‘Europe‘,‘Africa‘,‘Oceania‘,‘America‘) collate
ÃÂ ÃÂ ÃÂ utf8_bin NOT NULL,
ÃÂ ÃÂ ÃÂ PRIMARY KEYÃÂ (`id`)
ÃÂ ÃÂ )
Facciamoci puntare l‘applicazione appena creata (vedi Ruby III parte per i dettagli sulla configurazione… questo solo se non leggi tutti gli articoli su Ruby…).
In particolare ci riferiamo al file create.sql, presente nella cartella db, dove è contenuto lo script di creazione del database e al file database.yml dove ci sono i puntamenti veri e propri dei tre ambienti (sviluppo, test, produzione).
Impostati i parametri delle connessioni al database la view ‘data navigator‘ di RadRails ci consente di effettuare il browsing delle tabelle del database e, in un contesto in cui l‘ORM utilizzato (ActiveRecord) usa un mapping implicito dei campi delle tabelle, questo risulta essere di fondamentale importanza (per non impazzire con i nomi dei campi!!!).
A questo punto passiamo alla creazione del nostro controller. A differenza dell‘articolo precedente non ricorriamo all‘utilizzo di una “scaffold application” ma provvediamo puntualmente alla creazione di ogni componente dell‘applicazione di cui abbiamo bisogno ricorrendo a qualche piccolo intervento in termini di codice mancante al completamento dell‘applicazione (situazione abituale in contesto in cui è il cliente che decide i requisiti funzionali del progetto).
Creiamo il nostro model facendo il mapping della tabella ‘Countries‘. Utilizzando RadRails abbiamo a disposizione, per far questo, una ‘view Generators‘
Figura 3 – Creazione del model con il generator di RadRails
Scegliamo ‘model‘ come primo parametro e impostiamo il nome della tabella come ulteriore parametro. Clicchiamo su ‘Go‘ e attendiamo il generatore che faccia il proprio dovere.
Abbiamo così ottenuto il file countries.rb che per il solo fatto di contenere una classe che estende ActiveRecord::Base mappa la tabella con il nome corripondente (di default Rails prende il nome come riferimento tra model e tabella occupandosi lui di fare il plurale nel nome delle tabelle) e una serie di file che costituiscono lo skeleton per le operazioni di test.
È nostra intenzione mettere a disposizione un web service per fruire delle informazioni contenute nella tabella ‘Countries‘. Ricorriamo all‘utilizzo del ‘generator‘ con il parametro ‘web_service‘. Questo consentirà di generare il controller e la struttura necessaria alla fruizione del web service (con la medesima signature).
Figura 4 – Creazione del web service con il generatorÃÂ di RadRails.
Notiamo che sono stati creati una serie di files tra i quali:
{rails-project-root}appapiscountry_api.rb
{rails-project-root}appcontrollercountry_controller.rb
Nel primo sono contenute le signature dei metodi, nel secondo le implementazioni dei servizi che saranno resi disponibili via web services. Concentriamoci sul metodo ‘find_all_countries‘. Apriamo country_api.rb e specifichiamo la signature del nostro servizio. Attualmente il contenuto del file è il seguente skeleton:
ÃÂ class CountryApi < ActionWebService::API::Base
ÃÂ ÃÂ api_method :find_all_countries
ÃÂ end
Lo integriamo in modo tale che diventi:
ÃÂ class CountryApi < ActionWebService::API::Base
ÃÂ ÃÂ api_method :find_all_countries,
ÃÂ ÃÂ ÃÂ :returns => [[:string]]
ÃÂ end
A questo punto il metodo ritornerà un array di ‘string‘. Ovviamente ‘:returns‘ definisce il ritorno del metodo. In questo caso non abbiamo parametri in input, altrimenti avremmo utilizzato la keyword ‘:expects‘ per dichiararli. Editiamo il file country_controller.rb, attualmente solo uno skeleton. Il file diventerà qualcosa del tipo
ÃÂ class CountryController < ApplicationController
ÃÂ ÃÂ wsdl_service_name ‘Country‘
ÃÂ ÃÂ web_service_api CountryApi
ÃÂ ÃÂ web_service_scaffold :invoke
ÃÂ ÃÂ def find_all_countries
ÃÂ ÃÂ ÃÂ Country.find(:all).map { |country| country.name}
ÃÂ ÃÂ end
ÃÂ end
Il metodo find_all_countries, che utilizza ActiveRecord per recuperare i dati dalla base dati, restituirà il nome della nazione.
A questo punto è giunto il momento di predisporre un test del nostro servizio. Editiamo il file {rails-project-root} estfunctionalcountry_api_test.rb. Il generatore ha predisposto quasi tutto il necessario per l‘effettuazione del test. Risulta mancante solo l‘implementazione del metodo. Verifichiamo, a puro scopo didattico, che l‘elemento recuperato è una stringa.
ÃÂ def test_find_all_countries
ÃÂ ÃÂ result = invoke :find_all_countries
ÃÂ ÃÂ assert result[0].is_a?(String)
ÃÂ end
Per lanciare il test dovremmo aggiungere il contenuto informativo della tabella ‘countries‘ destinato ad essere utlizzato durante l‘esecuzione. Quindi apriamo il file {rails-project-root} estfixturescountries.yml e aggiungiamo la nostra nazione di interesse (operazione già effettuata in RubyIII).
Facciamo lo start del server ‘MokabyteWSServer‘, attendiamo che sia “started” e lanciamo il nostro test test_list_all presente nel file country_api_test.rb. Per la gestione, utilizziamo gli strumenti messi a disposizione dall‘IDE.
Figura 5 – Eseguiamo il nostro test
Se il risultato dell‘esecuzione è qualcosa del tipo
ÃÂ Runs 1/1ÃÂ Errors 0ÃÂ Failures 0
nessun problema per il nostro Web Service. Notiamo che la ‘view‘ Test::TestUnit è molto comoda e utile per tenere sotto controllo l‘esecuzione e la ripetizione dei test da un unico punto.
Andiamo a vedere il nostro wsdl e …ÃÂ anche qualche altra funzionalità messa a disposizione dal framework. Apriamo il browser e puntiamo all‘indirizzo
http://localhost:3000/Country/service.wsdl
Figura 6 – Il wsdl del servizio
Abbiamo il wsdl per tutte le operazioni di condivisione di cui potremmo aver bisogno. Ora puntiamo a
http://localhost:3000/Country/invoke.
Figura 7 – L‘unico servizio che abbiamo reso disponibile
Abbiamo l‘elenco dei metodi resi disponibili nel nostro servizio.
Clicchiamo su FindAllCountries(), otteniamo una pagina in cui possiamo scegliere il protocollo (SOAP o XML-RPC). Scegliendo SOAP otteniamo in visualizzazione i return value (il contenuto del campo name della tabella countries), l‘XML di richiesta e quello in risposta.
Figura 8 – Invocazione del servizio e risultati dell‘invocazione
Niente di più semplice. Siamo pronti per esporre i nostri servizi.
Consumiamo i servizi di un Web Service
Passiamo alla seconda applicazione. Innanzitutto occupiamoci di fruire i servizi del servizio appena messo a disposizione. Sembrerebbe che AWS lasci un pò a desiderare in ambito relativo al consumo di Web Service (in effetti a noi non ha funzionato correttamente) e quindi abbiamo optato per la libreria SOAP::WSDLDriverFactory presente nel pacchetto base.
Creiamo un progetto Ruby e lo chiamiamo ‘MokabyteWSClient‘ con il solito iter File -> New -> Ruby Project. Per la fruizione abbiamo bisogno di due file. Il file countries_api.rb (lo stesso del progetto precedente) dove sono contenute le signature dei metodi e il wsclient.rb con le seguenti informazioni.
ÃÂ require ‘soap/wsdlDriver‘
ÃÂ require ‘cgi‘
ÃÂ endpoint = ‘http://localhost:3000/country/service.wsdl‘
ÃÂ soap = SOAP::WSDLDriverFactory.new(endpoint).create_rpc_driver
ÃÂ countries = soap.FindAllCountries
Il significato del codice risulta essere piuttosto intuitivo. Proviamo a lanciare il nostro wsclient.rb, magari mettendo un punto di debug e facendo un ‘inspect‘ della variabile ‘countries‘.
Figura 9 – Inspect della variabile countries dopo l‘invocazione del web service remoto
Il risultato dovrebbe essere qualcosa di molto simile a quanto sopra riportato.
Conclusioni
Basando la nostra procedura di sviluppo su RadRails, ci siamo occupati di generare model, controller, web services e abbiamo poi provveduto a effettuare le mancanti operazioni di ‘knitting‘ necessarie all‘applicazione per funzionare come richiesto. Quindi siamo passati alla fruizione del web service.
Abbiamo testato il tutto con estrema facilità e tenendo tutto (test, debug, database) sotto controllo dall‘ l‘IDE. Un IDE che si sta evolvendo molto rapidamente. A testimonianza del grosso fermento che si sta muovendo intorno al mondo Ruby/RoR è notizia recente che Aptana, l‘IDE, basato su Eclipse (prevalentemente orientata a sviluppi Ajax) ha inglobato le features che erano di RadRails. Qualche altro vendor di rilievo, tra i quali Borland (http://www.codegear.com/products/rubyide), comincia a puntare su Ruby on Rails.
Questo fermendo intorno al mondo RoR ci fa sicuramente piacere e ci spinge a continuare in questa direzione.
Riferimenti
[1] Dave Thomas, “Programming Ruby. The Pragmatic Programmer‘s Guide”, Pragmatic Bookshelf, 2004
http://www.pragmaticprogrammer.com
[2] Dave Thomas, “Agile Software Development with Rails. The Pragmatic Programmer‘s Guide”, Pragmatic Bookshelf, 2005
http://www.pragmaticprogrammer.com
[3] RadRails (redirect al sito di Aptana)
http://www.radrails.org
[4] Aptana IDE + Rails
http://aptana.com/download_rails_rdt.php
[5] Code Gear
http://www.codegear.com/products/rubyide
[6] Calling a .NET Web Service From Rails
http://webgambit.com/archive/2007/01/10/calling-a-net-web-service-from-rails-original.aspx