3 votes

question sur le passage de paramètres en Ruby

Comparons les deux extraits de code suivants :

class Logger 
  def self.add_logging(id_string)
    define_method(:log) do |msg| 
      now = Time.now.strftime("%H:%M:%S")
      STDERR.puts "#{now}-#{id_string}: #{self} (#{msg})"
    end 
  end
end 

class Song < Logger
  add_logging "Tune" 
end

song = Song.new 
song.log("rock on")

class Logger
  def self.add_logging(id_string)
    def log(msg)
      now = Time.now.strftime("%m")
      puts "#{now}-#{id_string}: #{self}(#{msg})"
    end
  end
end

class Song < Logger
  add_logging "Tune"
end

s = Song.new

s.log("can't smile with you")
#=> NameError: undefined local variable or method `id_string' for #<Song:0x000001018aad70>

Je n'arrive pas à comprendre pourquoi le deuxième cas obtient l'erreur NameError et pourquoi la chaîne id_string ne peut pas lui être transmise.

3voto

7stud Points 117

Un def crée une nouvelle portée, ce qui n'est pas le cas d'un bloc. Une nouvelle portée coupe la visibilité des variables environnantes. ruby dispose de deux autres "créateurs de nouvelles portées" : class et module.

x = 10

3.times do |i|
  puts i * x
end

def do_stuff
  puts x * 10
end

do_stuff  

--output:--
0
10
20
`do_stuff': undefined local variable or method `x'

1voto

merryprankster Points 1397

Id_string est local à la méthode add_logging. Dans votre dernière implémentation, la méthode log ne peut pas la voir, d'où l'erreur. Dans la première implémentation, vous définissez dynamiquement log-method à l'intérieur add_logging.

En d'autres termes, une variable locale est visible à l'intérieur de la portée dans laquelle elle est définie (dans ce cas, une méthode). Dans cette dernière implémentation, vous avez des portées imbriquées (= une déclaration de méthode à l'intérieur d'une méthode), et la portée intérieure ne peut pas accéder aux variables qui sont locales à la portée extérieure.

Comme le suggère la réponse de @stef, vous pouvez contourner ce problème en élargissant la portée de la variable. Je recommanderais de garder la portée des variables aussi "serrée" que possible, et je préfère donc votre première implémentation.

0voto

stef Points 6478

Essayez ceci avec une variable de classe ?

class Logger 
  def self.add_logging(id_string)
    @@my_id = id_string
    define_method(:log) do |msg| 
      now = Time.now.strftime("%H:%M:%S")
      STDERR.puts "#{now}-#{@@my_id}: #{self} (#{msg})"
    end 
  end
end

0voto

7stud Points 117

Les variables de classe doivent être évitées en ruby en raison de leur nature problématique. La solution en Ruby est d'utiliser des variables d'instance de classe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X