比较以下两个代码段:
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>
我无法弄清楚为什么第二种情况会出现NameError错误,以及无法将id_string传递给它的原因。
答案 0 :(得分:3)
def创建一个新范围;一个街区没有。新范围会切断周围变量的可见性。 ruby还有另外两个“新范围创建者”:类和模块。
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'
答案 1 :(得分:1)
id_string是方法add_logging的本地。在后一种实现中,log-method无法看到它,因此出现错误。在前一个实现中,您可以在 add_logging中动态定义log-method 。
换句话说,局部变量在其定义的范围内是可见的(在本例中是一种方法)。在后一个实现中,您有嵌套的作用域(=方法中的方法声明),而内部作用域不能访问外部作用域的本地变量。
正如@stef在回答中所建议的那样,你可以解决这个我扩大变量范围的问题。我建议尽量保持变量范围“紧”,因此更喜欢你的第一个实现。
答案 2 :(得分:0)
尝试使用类变量吗?
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
答案 3 :(得分:0)
由于红宝石存在问题,因此应避免使用类变量。 ruby方式是改为使用“类实例变量”。