question about parameter passing in Ruby
Comparing the following two code snippets:
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>
I can't figure out why the second case gets the NameError error, and why the id_string can't be passed to it.
A def creates a new scope; a block does not. A new scope cuts off the visibility of the surrounding variables. ruby has two other 'new scope creators': class and 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'
id_string is local to method add_logging. In your latter implementation, log-method can not see it, hence the error. In the former implementation, you dynamically define log-method within add_logging.
In other words, local variable is visible within the scope it is defined in (in this case, a method). In that latter implementation, you have nested scopes (=a method declaration within a method), and inner scope can not access variables that are local to outer scope.
As suggested in answer by @stef, you might get around this my widening the scope of the variable. I would recommend keeping variable scopes as 'tight' as possible, and therefore prefer your first implementation.
Try this with a class variable?
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
Class variables should be avoided in ruby due to their problematic nature. The ruby way is to use 'class instance variables' instead.
精彩评论