PDI^2

Mock, MMD e la open() che vorrei

ruby, oop April 26, 2006 6:19 am (Save post)

Questo post si basa sul piccolo lavoro fatto la volta scorsa per creare special case nei metodi .
Avendo a disposizione quello strumento posso affrontare una delle Grandi Domande: mi conviene fare refactoring per rendere il codice più facilmente testabile se posso evitarlo?

Nel dettaglio: ho uno script che prende in automatico dei dati da risorse su web, e poi fa un grafico con i cambiamenti, insomma una scopiazzatura di EhList, fatta per giocare con le api di Yahoo! SiteExplorer in ruby

Poiché i servizi sono tutti differenti esiste un metodo specifico per accedere ad ognuno di essi, catturato nell’esistenza di una classe Provider e di varie sottoclassi, ognuna con una descrizione, un metodo specifico per recuperare le informazioni remote ed eventuali altre cose.

Un esempio di provider è:

class YahooProvider < Provider
  DESC=\"Yahoo SE inbound links\"
  APPID=\"myAppId\"
  URL=\"url yahoo SE?query=%s&appid=%s\"
  def initialize(name)
    text=open(URL%[name,APPID]).read
    @result=Mapping.xml2obj(text)
  end
  def results_size
    @result.xmlattr_totalResultsAvailable
  end
end

se state imprecando contro di me perché non chiudo la connessione aperta con open() potete calmarvi, lo script completa le operazioni in meno di un minuto e si chiude, il che fa si che le risorse vengano comunque recuperate.

Ora: come testare questo provider? In particolare, come testare l’operazione di parsing dell’xml?
La cosa più ovvia è passargli dei dati preconfezionati, magari presi da un’esecuzione precedente dello script, e verificare che li interpreti correttamente.
Solo che è il metodo initialize ad eseguire sia la letttura dei dati che il parsing.
La soluzione che permette di testare meglio tutto ciò è di spostare queste funzionalità in un altro posto per poterle testare in isolamento, con una cosa come questa:

  def read_data()
    open(URL%[name,APPID]).read
  end
  def parse(xml)
    Mapping.xml2obj(xml)
  end
  def initialize(name)
    testo=read_data()
    @result= parse(testo)
  end

Se è vero che questo approccio introduce una certa flessibilità è anche vero che si tratta di una flessibilità non necessaria, o meglio di una fattorizzazione del codice in più parti che però non è utile in quanto non userò mai una delle funzionalità separatamente dall’altra.
Insomma, sto facendo una cosa di cui non ho bisogno solo per far felici i test, che non è una cosa terribile, ma la eviterei.

Ma, se siete arrivati a leggere fin qui probabilmente sapete già cosa possiamo fare in questo caso:

require 'stringio'
special Kernel, \"open\" do
  StringIO.new(\"xml di prova\")
end
	
assert_equal valore_atteso, YahooProvider.new(url).results_size

è un hack, e non è molto nella filosofia di come dovrebbero essere i test probabilmente..
ma funziona e non aggiunge complicazioni al codice originale.
La domanda è: cosa è giusto fare? E’ giusto perseguire un approccio alla scrittura di test che è tipico in altri ambienti anche se in questo caso ne posso fare a meno sfruttando le potenzialità di ruby?

Infine, se vi state chiedendo che cosa significhi la frase relativa ad open() nel titolo, sappiate che stavo pensando che sarebbe molto carino poter usare qualcosa come
open(”mock://foo”), ed in realtà è possibile farlo con open-uri.rb, magari in futuro ne parlerò. Però non è builtin, uffa :(

Piccole riflessioni sulla OOP

oop February 5, 2006 10:15 pm (Save post)

Questo è un blog-articolo interessante su alcuni approcci differenti alla programmazione , contrapponendo multiple & single dispatch, classi & prototipi, oggetti & closure.

Contiene anche vari link interessanti, anche se mi pare di averli già visti quasi tutti.
Il che non significa che li abbia guardati con attenzione ed assolutamente è lontano dall’idea che io possa averli capiti, so solo che esistono.

Consiglio in particolare di vedersi i video sul Self System, che tra l’altra fa venire da chidersi come mai Sun abbia deciso di abbandonare una tecnologia rivoluzionaria per spingere java.

L’interfaccia umana: come Java ha rovinato la mia mente

ruby, oop, java, personale December 23, 2005 11:16 am (Save post)

Nella blogosfera di recente c’è stata una specie di flamefest riguardo l’interfaccia fornita dalla classe Array in ruby.
In pratica Martin Fowler è contento perché questa classe fornisce un’interfaccia molto ricca in ruby, mentre qualche hard core javanese ritiene che sia una follia mettere più di dodici metodi in una classe. (more…)

Design Pattern in $linguaggio

ruby, python, oop, php November 4, 2005 5:27 pm (Save post)

Dopo un bel po’ di tempo che ci perdevo tempo sono finalmente riuscito a mettere online un articoletto su cui stavo trafficando da un bel po’, una specie di analisi parallela dell’Abstract Factory pattern in Common Lisp/Ruby/Python/etc.

Io sono un sostenitore dell’ipotesi che i DP servano solo se il linguaggio è tonto ,per qualche valore di tonto, ed in questo singolo caso in esame sembrerebbe che l’Abstract Factory sia necessario effettivamente solo per valori abbastanza grandi di tontezza.

Però ho in mente qualche altro pattern che invece forse sarebbe necessario comunque anche in python e ruby, come il visitor (che però è ancora inutile in CL). Insomma in realtà è unindagine interessante.
Specie se riesco ad infilarci dentro anche Haskell :D

Comunque, l’articolo non è venuto bene quanto volevo per mancanza di tempo, ma se non mi sbrigavo a passarlo a lm non l’avrei finito mai.
Ora devo mettermi a scrivere il secondo, (e scrivere la tesi e finire il sito del gruppo utenti ruby italia, e finire quell’applicazione in j2me per imparare i kanji, e sistemare il look di questo blog e …)

Ragioni per cui amo ruby numero 1002, 1003

ruby, oop, java October 30, 2005 9:39 pm (Save post)

Ispirato da una discussione su wup.it, mi sono tornati in mente questi due esempi del perché preferisco ruby a java:

Come mettere un array di tipi base in una lista
Sostanzialmente, l’unico modo (che io conosca) è farlo a mano.
Riscriversi un bel ciclo ogni volta, error prone come piace a noi.
Si noti che esiste un Arrays.asList(Object[]) ma visto che i tipi base in java non sono oggetti, non funziona.
Non solo, per quel che ne so non è neppure possibile scrivere un metodo generico che trasformi un array in una List, in quanto a livello di linguaggio non c’è un’associazione tra i tipi primitivi e le rispettive classi Boxed.
Ovviamente si può risolvere con una sfilza di if/else ma non è che sia un granché come soluzione.

null non è un oggetto
Il che significa che non posso scrivere null.equals(foo).
Il che a sua volta significa, ad esempio, che un metodo per verificare l’uguaglianza tra due oggetti Pair, cioè dei contenitori di coppie di numeri, in ruby sarebbe:

 class Pair
  def == (other)
   first == other.first  and  second == other.second
  end
 end

mentre in java viene una cosa come questa (scrollare fino alla riga 43, poi sono 42 righe di codice, java5, la versione che dovrebbe semplificare tutto) [link alternativo senza note/sintassi colorata].
E c’è chi dice che non avere distinzioni tra tipi primitivi e non sia una cosa buona :D

Get free blog up and running in minutes with Blogsome
Theme designed by Janis Joseph