.. _bag_book/trigger_resolver/resolver: Bag dinamiche e Resolver ======================== Introduzione ------------ Una bag oltre a valori scalari, vettoriali ed altre bag può contenere un oggetto dinamico che si chiama *resolver* ovvero un qualsiasi oggetto che erediti dalla classe ``BagResolver``. Un resolver è un oggetto dinamico che restituisce il risultato di una chiamata a funzione come se fosse un valore realmente contenuto nella bag. Quando una bag contiene o è costituita da elementi dinamici viene anche definita **Bag dinamica**. Possiamo avere Bag dinamiche che contengono * chiamate a query su un database * chiamate a webservice * chiamate a funzioni che calcolano valori in tempo reale * chiamate al sistema operativo O qualunque altra cosa vi possa venire in mente quando definite un resolver BagResolver ----------- L'idea del resolver è quella di nascondere le chiamate esplicite, definendo invece un oggetto che le esegue quando si accede ad un *path* della bag e che ne restituisce il risultato come se fosse un valore statico, in modo del tutto trasparente. Un resolver fa l'override della primitiva ``__call__`` ed implementa il metodo load() e quando si accede ad un elemento resolver viene invocato. Un resolver però può avere anche una cache, utile specialmente se la chiamata è lenta o onerosa. Se si imposta il parametro *cacheTime* viene salvata localmente all'indirizzo del resolvere il risultato dell'ultima chiamata e questo viene restituito a tutti gli accessi successivi finché non scade il tempo di cache. Il valore di default del *cacheTime* è 0. Un resolver deve implementare il metodo ``load()`` Esempio 1: TimeResolver ----------------------- Un esempio molto semplice di resolvere è il TimeResolver :: from datetime import datetime from gnr.core.gnrbag import Bag, BagResolver class TimeResolver(BagResolver): def __call__(self): return datetime.now() Ora proviamo ad usarlo >>> mybag = Bag() >>> mybag['now'] = TimeResolver() >>> print mybag['now'] 2010-11-18 11:47:13.237443 Se impostiamo un cacheTime pari a 100. Il valore verrà aggiornato solo ogni 100ms. >>> ct=100 >>> mybag['now']=TimeResolver(cacheTime=ct) >>> print mybag['now'] 2010-11-18 11:49:34.257631 Esempio 2: UserInfoResolver --------------------------- In questo resolver il risultato è una Bag contenente alcune informazioni prese da dalle librerie **os** e **socket** ( hostname, IP, PID, user). :: from gnr.core.gnrbag import Bag, BagResolver import socket, os class UserInfoResolver(BagResolver): def load(self): result = Bag() try: result['hostname']=socket.gethostname() result['ip']=socket.gethostbyname(result['hostname']) except: result['hostname']='localhost' result['ip']='unknown' result['pid']=os.getpid() result['user']=os.getenv('USER') result['ID']=result['ip']+'-'+str(result['pid'])+'-'+result['user'] return result Vediamo il resolver all'opera. >>> mybag = Bag() >>> mybag['info'] = UserInfoResolver() >>> info = mybag['info'] >>> template = "This is the process %s.\nYou are user %s, from the host %s at the address %s" >>> print template %(mybag['info.pid'], mybag['info.user'], mybag['info.hostname'], mybag['info.ip']) Esempio 3: RssFeedResolver -------------------------- Questo resolver riceve l'URL di un RSS feed e, dal momento che si popola facilmente una bag a partire da un XML è molto semplice gestire il risultato. :: class RssFeedResolver(BagResolver): def init(self, feed): self.feed=feed def load(self): feed= Bag(self.feed)['rss.channel'] result= Bag() result['title']= feed.pop('title') result['description']= feed.pop('description') result['link']= feed.pop('link') result['language']= feed.pop('language') result['copyright']= feed.pop('copyright') dig= feed.digest('#v.title, #v.description, #v.pubDate, #v.link') news=Bag() for title, description, pubDate, link in dig: news.setItem(title.replace('.', '\.').replace(' ','_'),description,link=link, date=pubDate, title=title) result['news']=news return result Ecco come funziona il resolver:: >>> mybag['feeds.washingtonpost']= RssFeedResolver('http://www.washingtonpost.com/wp-dyn/rss/world/index.xml') La Bag risultante è strutturata come segue: +--------------------+------------------------------------------------------------------------------------------------------+ | **item** | **value** | +====================+======================================================================================================+ | `title` | washingtonpost.com - World News and Analysis From The Washington Post | +--------------------+------------------------------------------------------------------------------------------------------+ | `description` | World news headlines from the Washington Post,including international news and opinion from Africa, | | | North/South America,Asia,Europe and Middle East. Features include world weather, news in Spanish, | | | interactive maps, daily Yomiuri and Iraq coverage. | +--------------------+------------------------------------------------------------------------------------------------------+ | `link` | http://www.washingtonpost.com/wp-dyn/content/world/index.html?nav=rss_world | +--------------------+------------------------------------------------------------------------------------------------------+ | `language` | EN-US | +--------------------+------------------------------------------------------------------------------------------------------+ | `copyright` | None | +--------------------+------------------------------------------------------------------------------------------------------+ | `news` | Bag of News | +--------------------+------------------------------------------------------------------------------------------------------+ Ogni item è un *BagNode* strutturato come segue: +--------------------------------------+-----------------------------------------------------------------+---------------------+ | label | value | attributes | +======================================+=================================================================+=====================+ | In_Russia,_A_Secretive_Force_Widens_ | MOSCOW - On Nov. 15, the Russian Interior Ministry and Gazprom, | link, date, title | | | the state-controlled energy giant, announced... | | +--------------------------------------+-----------------------------------------------------------------+---------------------+ Scorciatoia: il BagCbResolver ------------------------------ In realtà se il risultato che deve essere ritornato è una semplice chiamata a funzione si può evitare di definire una classe Resolver apposta. E si può usare una istanza del più generico ``BagCbResolver`` che è un BagResolver che chiama una funzione callback passata come parametro. :: >>> from gnr.core.gnrbag import Bag, BagCbResolver >>> from datetime import datetime >>> mybag = Bag() >>> mybag['now'] = BagCbResolver(datetime.now) >>> print mybag['now'] 2010-11-18 14:23:40.070095 Questa scorciatoia funziona con tutte le funzioni callback. :: def sayHello(): return 'Hello World!' >>> mybag['hello'] = BagCbResolver(sayHello) >>> print mybag['hello'] Hello World! In alternativa si può usare il metodo ``setCallBackItem`` :: >>> mybag.setCallBackItem('hello', sayHello) >>> print mybag['hello'] Hello World! .. sectionauthor:: Giovanni Porcari