avendo finalmente un filo di tempo libero, vorrei parlare di un interessante problemino di approccio agli Unit Test.

Ma al momento questo margine è troppo piccolo, quindi per ora mi limito a lasciarvi ponderare questo spezzone di codice:

  def special(mod, meth,*args, &block)
   prev = mod.instance_method(meth)
   mod.send :define_method, meth do |*a|
      if args == a
        block.call(*a)
      else
       prev.bind(mod).call(*a)
      end
   end
 end

i test sono in fondo al post.

In pratica è un modo particolarmente stupido ed inefficiente di definire degli special case nel comportamento di un metodo.

I metodi così definiti sono in qualche modo simili a dei multimetodi mooolto depotenziati e che non interagiscono con l’ereditarietà, il che fa si che si possano facilmente incrementare le prestazioni mettendo le varie versioni speciali in un Hash.
In questo modo, invece di avere l’overhead di chiamate a funzione ricorsive si avrebbe solo il costo dell’accesso per recuperare la versione corretta.

Ovviamente c’è poi il fattore di dove mettere questo hash.. insomma in realtà si tratta di un problemino interessante che vi consiglio di affrontare, magari come kata per esercitarvi con ruby.

Sul wiki di ruby italia potete vedere le soluzioni ad un problema analogo, quello di scrivere una funzione memoize che trasformi un metodo qualunque in uno memoizzato, ovvero uno che calcoli il risultato per un certo set di argomenti una sola volta, anche a fronte di numerose chiamate.

Per sapere cosa c’entri tutto questo con i test dovete aspettare che abbia di nuovo un attimo di tempo, intanto pensateci su, i test contengono degli hint :)

	
if __FILE__ == $0
  require 'test/unit'
  class TC_Special < Test::Unit::TestCase
    def test_one_mod
      special(Kernel, :o pen, \"speciale\") {\"atteso\"}
      assert_equal \"atteso\", open(\"speciale\")
      assert_raise(Errno::ENOENT){open \"\"}
    end
	
    def test_many_mod
      for i in %w(primo secondo terzo) do
        special(Kernel, :o pen, i) {i}
      end
      for i in %w(primo terzo secondo)
        assert_equal i, open(i)
      end
      assert_raise(Errno::ENOENT){open \"\"}
    end
	
    def test_one_class
      person=Struct.new :name
      special(person, :name=, \"01\") {raise}
      assert_raises(RuntimeError) { person.new.name= \"01\"}
    end
  end
end