Multiplexer

Vedi tutte le pagine e le modifiche recenti o scarica i sorgenti nella pagina


Cosa e’ un Multiplexer?

Un multiplexer e’ un oggetto che funziona moltiplicando un messaggio, e inviandolo a degli oggetti registrati presso di lui. L’utilita’ di un simile oggetto e’ evidente, ad esempio nel caso si desideri effettuare il log in un file di tutto cio’ che viene stampato a schermo, ma continuando a poter leggere anche i messaggi in tempo reale, si potrebbe usare un Multiplexer che nasconda STDOUT e un oggetto File. Questa e’ esattamente la funzione del comando tee di UNIX.

In ruby, l’implementazione e’ semplicissima:
 class Multiplexer
  def initialize(*oggetti)
   @ogg=oggetti
  end
  def method_missing(metodo,*args,&blk)
   @ogg.each { |o| o.send(metodo,*args,&blk) }
  end
 end
Cosa accade qui? Beh, un oggetto Multiplexer viene inizializzato passsandogli un array di elementi. Un Multiplexer non risponde a nessun metodo in particolare. Ogni volta che su di un tale oggetto viene chiamato un metodo a cui lui non sa come rispondere, viene richiamato method_missing. A questo metodo vengono passati:
  1. un simbolo che rappresenta il metodo chiamato (ad esempio puts() )
  2. un array contenente tutti i parametri passati al metodo
  3. un eventuale blocco
Noi non faremo altro che inviare questi messaggi ad ogni oggetto, con una semplice iterazione. Vediamo un esempio:
 >> require 'multiplexer'
 => true
 >> require 'stringio'
 => true
 >> f1,f2 = StringIO.new, StringIO.new
 => [#<StringIO:0x27e17a0>, #<StringIO:0x27e1650>]
 >> m=Multiplexer.new(f1,f2)
 => #<Multiplexer:0x27d9868 @ogg=[#<StringIO:0x27e17a0>, #<StringIO:0x27e1650>]>
 >> m.puts 'ciao!'
 => [#<StringIO:0x27e17a0>, #<StringIO:0x27e1650>]
 >> f1.string
 => "ciao!\n" 
 >> f2.string
 => "ciao!\n" 
 => nil

il file multiplexer.rb ovviamente contiene il nostro codice. StringIO rappresenta un ottimo modo per fare delle prove con un testo senza scriverlo. Infatti si tratta di una stringa che si comporta com un File (beh, come un IO per essere precisi :)

Notate pero’ una cosa: se gli oggetti non sono coerenti, questo sistema crolla:
 >> str,ary= 'ciao', [1]
 => ["ciao", [1]]
 >> m=Multiplexer.new(str,ary)
 => #<Multiplexer:0x27c57f8 @ogg=["ciao", [1]]>
 >> m << 'accodiamo'  # tutti e due gli oggetti rispondono a '<<'
 => ["ciaoaccodiamo", [1, "accodiamo"]]
 >> str
 => "ciaoaccodiamo" 
 >> ary
 => [1, "accodiamo"]
 >> m.upcase!   # un array non sa come diventare maiuscolo!
 NoMethodError: undefined method `upcase' for [1, "accodiamo"]:Array
        from ./multiplexer.rb:6:in `send'
        from ./multiplexer.rb:6:in `method_missing'
        from ./multiplexer.rb:6:in `each'
        from ./multiplexer.rb:6:in `method_missing'
        from (irb):16
Possiamo cambiare la definizione del nostro metodo per far si che funzioni nonostante gli errori, usando lo statement modifier per rescue. Uno statement modifier serve a cambiare il comportamento di un istruzione in certi casi. Ad esempio:
 print x if x == 'ciao'
Nel caso di rescue esso dipende dalla generazione di un eccezione. Basta quindi ridefinire method_missing() cosi’:
 def method_missing(met,*arg,&blk)
   @ogg.each do |o|
    o.send(met,*arg,&blk) rescue nil
   end
  end
che significa : ad ogni elemento di @ogg inviamo il messaggio met con i suoi parametri, se accade un errore recuperiamolo, e non facciamo nulla. Esempio:
 >> m=Multiplexer.new('ciao',[])
 => #<Multiplexer:0x2751ea8 @ogg=["ciao", []]>
 >> m.capitalize!
 => ["Ciao", []]
Updated on January 23, 2006 12:57 by Gabriele Renzi (151.42.223.60)