2010-02-18 7 views
17

Si un bloc est une fermeture, pourquoi ce code ne fonctionne-t-il pas et comment le faire fonctionner?La fermeture ne fonctionne pas

def R(arg) 
    Class.new do 
    def foo 
     puts arg 
    end 
    end 
end 

class A < R("Hello!") 
end 

A.new.foo #throws undefined local variable or method `arg' for #<A:0x2840538> 
+0

Can nous voyons vos messages d'erreur? – samoz

+0

Un message d'erreur est sous commentaire dans l'exemple. – yukas

+0

@Earlz, merci pour l'édition. – yukas

Répondre

26

Les blocs sont des fermetures et arg est en effet disponible à l'intérieur du bloc Class.new. Il n'est tout simplement pas disponible dans la méthode foo car def démarre une nouvelle étendue. Si vous remplacez def avec define_method, qui prend un bloc, vous verrez le résultat que vous voulez:

def R(arg) 
    Class.new do 
     define_method(:foo) do 
      puts arg 
     end 
    end 
end 

class A < R("Hello!") 
end 

A.new.foo # Prints: Hello! 
+0

comment une méthode prenant des paramètres serait-elle définie? – Geo

+2

@Geo: Utiliser un bloc qui prend des paramètres. Par exemple. 'define_method (: add_one) fait | x | x + 1 extrémité' – sepp2k

5

Si vous définissez la classe dynamique, vous pouvez le modifier comme vous le souhaitez:

def R(arg) 
    c = Class.new 

    # Send the block through as a closure, not as an inline method 
    # definition which interprets variables always as local to the block. 
    c.send(:define_method, :foo) do 
    arg 
    end 

    c 
end 

class A < R("Hello!") 
end 

puts A.new.foo.inspect 
# => "Hello!"