2011-07-20 101 views
3

比较下面两段代码:问题传递红宝石

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

试试这个类变量?

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 
1

id_string是方法add_logging的本地方法。在你的后一种实现中,日志方法不能看到它,因此错误。在前一个实现中,您可以在 add_logging内动态定义日志方法

换句话说,局部变量在其定义的范围内(在这种情况下是方法)是可见的。在后一种实现中,您有嵌套的作用域(=方法中的方法声明),并且内部作用域不能访问局部于外部作用域的变量。

正如@stef的回答中所建议的那样,您可能会解决这个问题,因为我扩大了变量的范围。我建议尽可能将变量作用域保持为'紧密',因此更喜欢你的第一个实现。

3

def创建一个新的范围;一个块不会。新的范围会切断周围变量的可见性。红宝石还有另外两个'新的范围创建者':类和模块。

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' 
0

类别变量应该避免在红宝石由于其问题性质。 ruby的方式是使用'类实例变量'来代替。