2010-11-17 4 views
1

J'essaie de définir un DSL où les règles (pour le bien de cet exemple, les règles définissent si quelque chose est "bon" ou "mauvais") sont spécifiées dans un bloc dans Ruby. Ce qui suit est une version (grossièrement simplifiée) de ce que je veux faire:Retour de la méthode block - from appelée dans le bloc?

def test_block 
    # Lots of other code 
    is_good = yield # ... should give me true or false 
    # Lots of other code 
end 

test_block do 
    good if some_condition 
    good if some_other_condition 
    bad 
end 

Est-il possible que je peux définir des méthodes et goodbad qui font la pause bloc? Dans l'exemple ci-dessus, je veux:

  • vérifier si some_condition est vrai, et si elle est, sortir du bloc et l'ont return true
  • vérifier si some_other_condition est vrai, et si elle est, sortir du bloc et l'ont return true
  • return false du bloc sans condition si nous sommes encore dans le

-à-dire que je veux faire se comportent de code ci-dessus comme si je devais writt en bloc comme ceci:

result = test_block do 
    break true if some_condition 
    break true if some_other_condition 
    break false 
end 

Mettre break dans la définition de la méthode bonne/mauvaise ne fonctionne évidemment pas. Existe-t-il un autre moyen d'atteindre le résultat souhaité ou devrais-je penser à une façon totalement différente d'y parvenir?

+0

Très pertinent: http://innig.net/software/ ruby/closures-in-ruby.rb –

Répondre

3

Vous pouvez déclencher une exception dans le bloc et intercepter cette exception.

module Tester 
    class Breaker < Exception; end 
    class GoodBreak < Breaker; end 
    class BaadBreak < Breaker; end 
end 

def test_block(name) 
    begin 
    yield 
    rescue Tester::Breaker=>e 
    case e 
     when Tester::GoodBreak then puts "All is well with #{name}" 
     when Tester::BaadBreak then puts "BAD STUFF WITH #{name}" 
     else raise 
    end 
    end 
end 

def good; raise Tester::GoodBreak; end 
def bad; raise Tester::BaadBreak; end 

test_block('early out') do 
    good if true 
    good if puts("NEVER SEE THIS") || true 
    bad 
end 

test_block('simple pass') do 
    good if false 
    good if puts("SEE THIS FROM PASS TEST") || true 
    bad 
end 

test_block('final fail') do 
    good if false 
    good if puts("SEE THIS BUT PUTS IS NIL") 
    bad 
end 

#=> All is well with early out 
#=> SEE THIS FROM PASS TEST 
#=> All is well with simple pass 
#=> SEE THIS BUT PUTS IS NIL 
#=> BAD STUFF WITH final fail 

Voici un autre exemple en utilisant throw/catch au lieu de raise/rescue (mis à jour pour transmettre une valeur de retour le long) (merci @jleedev!):

def test_block(name) 
    result = catch(:good){ catch(:bad){ yield } } 
    puts "Testing #{name} yielded '#{result}'", "" 
end 

def good; throw :good, :good; end 
def bad; throw :bad, :bad; end 

test_block('early out') do 
    good if true 
    good if puts("NEVER SEE THIS") || true 
    bad 
end 

test_block('simple pass') do 
    good if false 
    good if puts("SEE THIS FROM PASS TEST") || true 
    bad 
end 

test_block('final fail') do 
    good if false 
    good if puts("SEE THIS BUT PUTS IS NIL") 
    bad 
end 

#=> Testing early out yielded 'good' 
#=> 
#=> SEE THIS FROM PASS TEST 
#=> Testing simple pass yielded 'good' 
#=> 
#=> SEE THIS BUT PUTS IS NIL 
#=> Testing final fail yielded 'bad' 
+1

Cela pourrait être une meilleure application de 'throw' /' catch', puisque vous les utilisez pour le contrôle de flux. (Ou appelez/cc Heh.) –

+0

@jleedev Droit vous êtes! Cela fait trop longtemps que je les utilise. Voici [la documentation d'introduction sur l'utilisation de throw/catch] (http://phrogz.net/ProgrammingRuby/frameset.html?content=http%3A//phrogz.net/ProgrammingRuby/tut_exceptions.html%23chatchandthrow) de la première édition gratuite de programmation Ruby. – Phrogz

+0

Merci @Phrogz et @jleedev - Je pensais à utiliser raise mais un peu effrayé loin de la pensée après une vie d'être dit que les exceptions sont pour "très mauvaises situations" (tm) seulement;) Mais le 'lancer' /' attraper la façon de le faire se sent beaucoup moins mal! –

Questions connexes